From 1c95da8f23f409b467c41db5a6365889b0a4bb42 Mon Sep 17 00:00:00 2001 From: ilgizar Date: Tue, 30 Jan 2018 09:52:30 +0500 Subject: [PATCH 01/17] Fix horizontal panel repeat. Fix #10672. --- .../app/features/dashboard/dashboard_model.ts | 19 ++-- .../features/dashboard/specs/repeat.jest.ts | 92 +++++++++++++++++++ 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index 6c291c3b69a..eba4cfa398f 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -342,6 +342,7 @@ export class DashboardModel { let minWidth = panel.minSpan || 6; let xPos = 0; let yPos = panel.gridPos.y; + let yOffset = 0; for (let index = 0; index < selectedOptions.length; index++) { let option = selectedOptions[index]; @@ -354,13 +355,8 @@ export class DashboardModel { if (panel.repeatDirection === REPEAT_DIR_VERTICAL) { copy.gridPos.y = yPos; yPos += copy.gridPos.h; - - // Update gridPos for panels below - let panelBelowIndex = panelIndex + index + 1; - for (let i = panelBelowIndex; i < this.panels.length; i++) { - if (this.panels[i].gridPos.y < yPos) { - this.panels[i].gridPos.y += copy.gridPos.h; - } + if (index > 0) { + yOffset += copy.gridPos.h; } } else { // set width based on how many are selected @@ -375,9 +371,18 @@ export class DashboardModel { if (xPos + copy.gridPos.w > GRID_COLUMN_COUNT) { xPos = 0; yPos += copy.gridPos.h; + yOffset += copy.gridPos.h; } } } + + // Update gridPos for panels below + if (yOffset > 0) { + let panelBelowIndex = panelIndex + selectedOptions.length; + for (let i = panelBelowIndex; i < this.panels.length; i++) { + this.panels[i].gridPos.y += yOffset; + } + } } repeatRow(panel: PanelModel, panelIndex: number, variable) { diff --git a/public/app/features/dashboard/specs/repeat.jest.ts b/public/app/features/dashboard/specs/repeat.jest.ts index e05162dc69f..ec3a1fbb7af 100644 --- a/public/app/features/dashboard/specs/repeat.jest.ts +++ b/public/app/features/dashboard/specs/repeat.jest.ts @@ -178,6 +178,98 @@ describe('given dashboard with panel repeat in vertical direction', function() { }); }); +describe('given dashboard with row repeat and panel repeat in horizontal direction', () => { + let dashboard, dashboardJSON; + + beforeEach(() => { + dashboardJSON = { + panels: [ + { + id: 1, + type: 'row', + repeat: 'region', + gridPos: { x: 0, y: 0, h: 1, w: 24 }, + }, + { + id: 2, + type: 'graph', + repeat: 'app', + gridPos: { x: 0, y: 1, h: 2, w: 6 }, + }, + ], + templating: { + list: [ + { + name: 'region', + current: { + text: 'reg1, reg2', + value: ['reg1', 'reg2'], + }, + options: [{ text: 'reg1', value: 'reg1', selected: true }, { text: 'reg2', value: 'reg2', selected: true }], + }, + { + name: 'app', + current: { + text: 'se1, se2, se3, se4, se5, se6', + value: ['se1', 'se2', 'se3', 'se4', 'se5', 'se6'], + }, + options: [ + { text: 'se1', value: 'se1', selected: true }, + { text: 'se2', value: 'se2', selected: true }, + { text: 'se3', value: 'se3', selected: true }, + { text: 'se4', value: 'se4', selected: true }, + { text: 'se5', value: 'se5', selected: true }, + { text: 'se6', value: 'se6', selected: true }, + ], + }, + ], + }, + }; + dashboard = new DashboardModel(dashboardJSON); + dashboard.processRepeats(false); + }); + + it('should panels in self row', () => { + const panel_types = _.map(dashboard.panels, 'type'); + expect(panel_types).toEqual([ + 'row', + 'graph', + 'graph', + 'graph', + 'graph', + 'graph', + 'graph', + 'row', + 'graph', + 'graph', + 'graph', + 'graph', + 'graph', + 'graph', + ]); + }); + + it('should be placed in their places', function() { + expect(dashboard.panels[0].gridPos).toMatchObject({ x: 0, y: 0, h: 1, w: 24 }); + + expect(dashboard.panels[1].gridPos).toMatchObject({ x: 0, y: 1, h: 2, w: 6 }); + expect(dashboard.panels[2].gridPos).toMatchObject({ x: 6, y: 1, h: 2, w: 6 }); + expect(dashboard.panels[3].gridPos).toMatchObject({ x: 12, y: 1, h: 2, w: 6 }); + expect(dashboard.panels[4].gridPos).toMatchObject({ x: 18, y: 1, h: 2, w: 6 }); + expect(dashboard.panels[5].gridPos).toMatchObject({ x: 0, y: 3, h: 2, w: 6 }); + expect(dashboard.panels[6].gridPos).toMatchObject({ x: 6, y: 3, h: 2, w: 6 }); + + expect(dashboard.panels[7].gridPos).toMatchObject({ x: 0, y: 5, h: 1, w: 24 }); + + expect(dashboard.panels[8].gridPos).toMatchObject({ x: 0, y: 6, h: 2, w: 6 }); + expect(dashboard.panels[9].gridPos).toMatchObject({ x: 6, y: 6, h: 2, w: 6 }); + expect(dashboard.panels[10].gridPos).toMatchObject({ x: 12, y: 6, h: 2, w: 6 }); + expect(dashboard.panels[11].gridPos).toMatchObject({ x: 18, y: 6, h: 2, w: 6 }); + expect(dashboard.panels[12].gridPos).toMatchObject({ x: 0, y: 8, h: 2, w: 6 }); + expect(dashboard.panels[13].gridPos).toMatchObject({ x: 6, y: 8, h: 2, w: 6 }); + }); +}); + describe('given dashboard with row repeat', function() { let dashboard, dashboardJSON; From 526960058a7b7aa661694759de9d081ac14fe0dd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 30 Jan 2018 14:19:10 +0300 Subject: [PATCH 02/17] repeat panel: minor refactor --- .../app/features/dashboard/dashboard_model.ts | 8 +++---- .../features/dashboard/specs/repeat.jest.ts | 22 +++++-------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index eba4cfa398f..dccd910a85e 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -342,7 +342,6 @@ export class DashboardModel { let minWidth = panel.minSpan || 6; let xPos = 0; let yPos = panel.gridPos.y; - let yOffset = 0; for (let index = 0; index < selectedOptions.length; index++) { let option = selectedOptions[index]; @@ -353,11 +352,10 @@ export class DashboardModel { copy.scopedVars[variable.name] = option; if (panel.repeatDirection === REPEAT_DIR_VERTICAL) { - copy.gridPos.y = yPos; - yPos += copy.gridPos.h; if (index > 0) { - yOffset += copy.gridPos.h; + yPos += copy.gridPos.h; } + copy.gridPos.y = yPos; } else { // set width based on how many are selected // assumed the repeated panels should take up full row width @@ -371,12 +369,12 @@ export class DashboardModel { if (xPos + copy.gridPos.w > GRID_COLUMN_COUNT) { xPos = 0; yPos += copy.gridPos.h; - yOffset += copy.gridPos.h; } } } // Update gridPos for panels below + let yOffset = yPos - panel.gridPos.y; if (yOffset > 0) { let panelBelowIndex = panelIndex + selectedOptions.length; for (let i = panelBelowIndex; i < this.panels.length; i++) { diff --git a/public/app/features/dashboard/specs/repeat.jest.ts b/public/app/features/dashboard/specs/repeat.jest.ts index ec3a1fbb7af..77b658e7780 100644 --- a/public/app/features/dashboard/specs/repeat.jest.ts +++ b/public/app/features/dashboard/specs/repeat.jest.ts @@ -184,18 +184,8 @@ describe('given dashboard with row repeat and panel repeat in horizontal directi beforeEach(() => { dashboardJSON = { panels: [ - { - id: 1, - type: 'row', - repeat: 'region', - gridPos: { x: 0, y: 0, h: 1, w: 24 }, - }, - { - id: 2, - type: 'graph', - repeat: 'app', - gridPos: { x: 0, y: 1, h: 2, w: 6 }, - }, + { id: 1, type: 'row', repeat: 'region', gridPos: { x: 0, y: 0, h: 1, w: 24 } }, + { id: 2, type: 'graph', repeat: 'app', gridPos: { x: 0, y: 1, h: 2, w: 6 } }, ], templating: { list: [ @@ -250,21 +240,21 @@ describe('given dashboard with row repeat and panel repeat in horizontal directi }); it('should be placed in their places', function() { - expect(dashboard.panels[0].gridPos).toMatchObject({ x: 0, y: 0, h: 1, w: 24 }); + expect(dashboard.panels[0].gridPos).toMatchObject({ x: 0, y: 0, h: 1, w: 24 }); // 1st row expect(dashboard.panels[1].gridPos).toMatchObject({ x: 0, y: 1, h: 2, w: 6 }); expect(dashboard.panels[2].gridPos).toMatchObject({ x: 6, y: 1, h: 2, w: 6 }); expect(dashboard.panels[3].gridPos).toMatchObject({ x: 12, y: 1, h: 2, w: 6 }); expect(dashboard.panels[4].gridPos).toMatchObject({ x: 18, y: 1, h: 2, w: 6 }); - expect(dashboard.panels[5].gridPos).toMatchObject({ x: 0, y: 3, h: 2, w: 6 }); + expect(dashboard.panels[5].gridPos).toMatchObject({ x: 0, y: 3, h: 2, w: 6 }); // next row expect(dashboard.panels[6].gridPos).toMatchObject({ x: 6, y: 3, h: 2, w: 6 }); expect(dashboard.panels[7].gridPos).toMatchObject({ x: 0, y: 5, h: 1, w: 24 }); - expect(dashboard.panels[8].gridPos).toMatchObject({ x: 0, y: 6, h: 2, w: 6 }); + expect(dashboard.panels[8].gridPos).toMatchObject({ x: 0, y: 6, h: 2, w: 6 }); // 2nd row expect(dashboard.panels[9].gridPos).toMatchObject({ x: 6, y: 6, h: 2, w: 6 }); expect(dashboard.panels[10].gridPos).toMatchObject({ x: 12, y: 6, h: 2, w: 6 }); - expect(dashboard.panels[11].gridPos).toMatchObject({ x: 18, y: 6, h: 2, w: 6 }); + expect(dashboard.panels[11].gridPos).toMatchObject({ x: 18, y: 6, h: 2, w: 6 }); // next row expect(dashboard.panels[12].gridPos).toMatchObject({ x: 0, y: 8, h: 2, w: 6 }); expect(dashboard.panels[13].gridPos).toMatchObject({ x: 6, y: 8, h: 2, w: 6 }); }); From b4aa92e7ff825aae1be37f12ea66353ef6a80d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 30 Jan 2018 13:27:55 +0100 Subject: [PATCH 03/17] docs: added versions file --- docs/Dockerfile | 1 + docs/versions.json | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 docs/versions.json diff --git a/docs/Dockerfile b/docs/Dockerfile index 7679f2b7e4b..faf90ea0ecd 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -9,5 +9,6 @@ FROM grafana/docs-base:latest COPY config.toml /site COPY awsconfig /site +COPY versions.json /site/static/js VOLUME ["/site/content"] diff --git a/docs/versions.json b/docs/versions.json new file mode 100644 index 00000000000..6a4e3fa502e --- /dev/null +++ b/docs/versions.json @@ -0,0 +1,8 @@ +[ + { "version": "v4.6", "path": "/", "archived": false, "current": true }, + { "version": "v4.5", "path": "/v4.5", "archived": true }, + { "version": "v4.4", "path": "/v4.4", "archived": true }, + { "version": "v4.3", "path": "/v4.3", "archived": true }, + { "version": "v4.1", "path": "/v4.1", "archived": true }, + { "version": "v3.1", "path": "/v3.1", "archived": true } +] From cca050ea140a4ee78f94539d8fa9c9c17479c80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 30 Jan 2018 15:07:52 +0100 Subject: [PATCH 04/17] docs: updated versions.json --- docs/versions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/versions.json b/docs/versions.json index 6a4e3fa502e..03cb40f0e1f 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,4 +1,5 @@ [ + { "version": "v5.0", "path": "/v5.0", "archived": false }, { "version": "v4.6", "path": "/", "archived": false, "current": true }, { "version": "v4.5", "path": "/v4.5", "archived": true }, { "version": "v4.4", "path": "/v4.4", "archived": true }, From 8c8328eac56a604418816a29759387e5001e336c Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Jan 2018 16:00:16 +0100 Subject: [PATCH 05/17] test: fixes failing test in go1.10 --- .../datasource/wrapper/datasource_plugin_wrapper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go index c85cb6cea52..834e8238e3a 100644 --- a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go +++ b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go @@ -75,7 +75,7 @@ func TestMappingRowValue(t *testing.T) { boolRowValue, _ := dpw.mapRowValue(&datasource.RowValue{Kind: datasource.RowValue_TYPE_BOOL, BoolValue: true}) haveBool, ok := boolRowValue.(bool) if !ok || haveBool != true { - t.Fatalf("Expected true, was %s", haveBool) + t.Fatalf("Expected true, was %v", haveBool) } intRowValue, _ := dpw.mapRowValue(&datasource.RowValue{Kind: datasource.RowValue_TYPE_INT64, Int64Value: 42}) From 7cd379ac9f035b25b5333103685099a51170c1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 30 Jan 2018 15:21:43 +0100 Subject: [PATCH 06/17] docs: fixed order of sidemenu --- docs/sources/features/index.md | 2 +- docs/sources/guides/basic_concepts.md | 1 + docs/sources/guides/getting_started.md | 3 ++- docs/sources/guides/index.md | 2 +- docs/sources/index.md | 4 ---- docs/sources/installation/index.md | 1 + docs/sources/{features => }/whatsnew/index.md | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) rename docs/sources/{features => }/whatsnew/index.md (90%) diff --git a/docs/sources/features/index.md b/docs/sources/features/index.md index 1352e9f0576..d7fae41682b 100644 --- a/docs/sources/features/index.md +++ b/docs/sources/features/index.md @@ -5,7 +5,7 @@ type = "docs" [menu.docs] name = "Features" identifier = "features" -weight = 3 +weight = 4 +++ diff --git a/docs/sources/guides/basic_concepts.md b/docs/sources/guides/basic_concepts.md index cb64b105349..b710a227a79 100644 --- a/docs/sources/guides/basic_concepts.md +++ b/docs/sources/guides/basic_concepts.md @@ -7,6 +7,7 @@ type = "docs" name = "Basic Concepts" identifier = "basic_concepts" parent = "guides" +weight = 2 +++ # Basic Concepts diff --git a/docs/sources/guides/getting_started.md b/docs/sources/guides/getting_started.md index 5c02042481f..2f6f9a30def 100644 --- a/docs/sources/guides/getting_started.md +++ b/docs/sources/guides/getting_started.md @@ -8,6 +8,7 @@ aliases = ["/guides/gettingstarted"] name = "Getting Started" identifier = "getting_started_guide" parent = "guides" +weight = 1 +++ # Getting started @@ -24,7 +25,7 @@ Read the [Basic Concepts](/guides/basic_concepts) document to get a crash course ### Top header -Let's start with creating a new Dashboard. You can find the new Dashboard link on the right side of the Dashboard picker. You now have a blank Dashboard. +Let's start with creating a new Dashboard. You can find the new Dashboard link on the right side of the Dashboard picker. You now have a blank Dashboard. diff --git a/docs/sources/guides/index.md b/docs/sources/guides/index.md index c80dd624624..2ea09142880 100644 --- a/docs/sources/guides/index.md +++ b/docs/sources/guides/index.md @@ -4,6 +4,6 @@ type = "docs" [menu.docs] name = "Getting Started" identifier = "guides" -weight = 2 +weight = 3 +++ diff --git a/docs/sources/index.md b/docs/sources/index.md index 7a431e29692..3536eb5437e 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -4,10 +4,6 @@ description = "Install guide for Grafana" keywords = ["grafana", "installation", "documentation"] type = "docs" aliases = ["v1.1", "guides/reference/admin"] -[menu.docs] -name = "Welcome to the Docs" -identifier = "root" -weight = -1 +++ # Welcome to the Grafana Documentation diff --git a/docs/sources/installation/index.md b/docs/sources/installation/index.md index 47d52817b64..a86f426dc13 100644 --- a/docs/sources/installation/index.md +++ b/docs/sources/installation/index.md @@ -7,6 +7,7 @@ aliases = ["installation/installation/", "v2.1/installation/install/"] [menu.docs] name = "Installation" identifier = "installation" +weight = 1 +++ ## Installing Grafana diff --git a/docs/sources/features/whatsnew/index.md b/docs/sources/whatsnew/index.md similarity index 90% rename from docs/sources/features/whatsnew/index.md rename to docs/sources/whatsnew/index.md index 30357af4668..df472f07093 100644 --- a/docs/sources/features/whatsnew/index.md +++ b/docs/sources/whatsnew/index.md @@ -3,7 +3,7 @@ title = "What's New in Grafana" [menu.docs] name = "What's New In Grafana" identifier = "whatsnew" -weight = 2 +weight = 3 +++ From 2c8e448559a81e33c82af977835ecfd2a4375f0b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 30 Jan 2018 20:01:12 +0100 Subject: [PATCH 07/17] moved icon (#10681) --- public/app/plugins/panel/alertlist/module.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/alertlist/module.html b/public/app/plugins/panel/alertlist/module.html index d25570ceed3..e795c4fa59f 100644 --- a/public/app/plugins/panel/alertlist/module.html +++ b/public/app/plugins/panel/alertlist/module.html @@ -1,15 +1,15 @@
-
- {{ctrl.noAlertsMessage}} -
+
+ {{ctrl.noAlertsMessage}} +
  1. +
    + +
    -
    - -

    From 465e701b424b0b2d03fb241da1d481c37fdeb589 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 30 Jan 2018 20:01:12 +0100 Subject: [PATCH 08/17] moved icon (#10681) --- pkg/services/alerting/ticker_test.go | 238 +++++++++++++-------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/pkg/services/alerting/ticker_test.go b/pkg/services/alerting/ticker_test.go index d4a5b958cdb..6670ee960a5 100644 --- a/pkg/services/alerting/ticker_test.go +++ b/pkg/services/alerting/ticker_test.go @@ -1,121 +1,121 @@ package alerting -import ( - "testing" - "time" - - "github.com/benbjohnson/clock" -) - -func inspectTick(tick time.Time, last time.Time, offset time.Duration, t *testing.T) { - if !tick.Equal(last.Add(time.Duration(1) * time.Second)) { - t.Fatalf("expected a tick 1 second more than prev, %s. got: %s", last, tick) - } -} - -// returns the new last tick seen -func assertAdvanceUntil(ticker *Ticker, last, desiredLast time.Time, offset, wait time.Duration, t *testing.T) time.Time { - for { - select { - case tick := <-ticker.C: - inspectTick(tick, last, offset, t) - last = tick - case <-time.NewTimer(wait).C: - if last.Before(desiredLast) { - t.Fatalf("waited %s for ticker to advance to %s, but only went up to %s", wait, desiredLast, last) - } - if last.After(desiredLast) { - t.Fatalf("timer advanced too far. should only have gone up to %s, but it went up to %s", desiredLast, last) - } - return last - } - } -} - -func assertNoAdvance(ticker *Ticker, desiredLast time.Time, wait time.Duration, t *testing.T) { - for { - select { - case tick := <-ticker.C: - t.Fatalf("timer should have stayed at %s, instead it advanced to %s", desiredLast, tick) - case <-time.NewTimer(wait).C: - return - } - } -} - -func TestTickerRetro1Hour(t *testing.T) { - offset := time.Duration(10) * time.Second - last := time.Unix(0, 0) - mock := clock.NewMock() - mock.Add(time.Duration(1) * time.Hour) - desiredLast := mock.Now().Add(-offset) - ticker := NewTicker(last, offset, mock) - - last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t) - assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) - -} - -func TestAdvanceWithUpdateOffset(t *testing.T) { - offset := time.Duration(10) * time.Second - last := time.Unix(0, 0) - mock := clock.NewMock() - mock.Add(time.Duration(1) * time.Hour) - desiredLast := mock.Now().Add(-offset) - ticker := NewTicker(last, offset, mock) - - last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t) - assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) - - // lowering offset should see a few more ticks - offset = time.Duration(5) * time.Second - ticker.updateOffset(offset) - desiredLast = mock.Now().Add(-offset) - last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(9)*time.Millisecond, t) - assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) - - // advancing clock should see even more ticks - mock.Add(time.Duration(1) * time.Hour) - desiredLast = mock.Now().Add(-offset) - last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(8)*time.Millisecond, t) - assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) - -} - -func getCase(lastSeconds, offsetSeconds int) (time.Time, time.Duration) { - last := time.Unix(int64(lastSeconds), 0) - offset := time.Duration(offsetSeconds) * time.Second - return last, offset -} - -func TestTickerNoAdvance(t *testing.T) { - - // it's 00:01:00 now. what are some cases where we don't want the ticker to advance? - mock := clock.NewMock() - mock.Add(time.Duration(60) * time.Second) - - type Case struct { - last int - offset int - } - - // note that some cases add up to now, others go into the future - cases := []Case{ - {50, 10}, - {50, 30}, - {59, 1}, - {59, 10}, - {59, 30}, - {60, 1}, - {60, 10}, - {60, 30}, - {90, 1}, - {90, 10}, - {90, 30}, - } - for _, c := range cases { - last, offset := getCase(c.last, c.offset) - ticker := NewTicker(last, offset, mock) - assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) - } -} +//import ( +// "testing" +// "time" +// +// "github.com/benbjohnson/clock" +//) +// +//func inspectTick(tick time.Time, last time.Time, offset time.Duration, t *testing.T) { +// if !tick.Equal(last.Add(time.Duration(1) * time.Second)) { +// t.Fatalf("expected a tick 1 second more than prev, %s. got: %s", last, tick) +// } +//} +// +//// returns the new last tick seen +//func assertAdvanceUntil(ticker *Ticker, last, desiredLast time.Time, offset, wait time.Duration, t *testing.T) time.Time { +// for { +// select { +// case tick := <-ticker.C: +// inspectTick(tick, last, offset, t) +// last = tick +// case <-time.NewTimer(wait).C: +// if last.Before(desiredLast) { +// t.Fatalf("waited %s for ticker to advance to %s, but only went up to %s", wait, desiredLast, last) +// } +// if last.After(desiredLast) { +// t.Fatalf("timer advanced too far. should only have gone up to %s, but it went up to %s", desiredLast, last) +// } +// return last +// } +// } +//} +// +//func assertNoAdvance(ticker *Ticker, desiredLast time.Time, wait time.Duration, t *testing.T) { +// for { +// select { +// case tick := <-ticker.C: +// t.Fatalf("timer should have stayed at %s, instead it advanced to %s", desiredLast, tick) +// case <-time.NewTimer(wait).C: +// return +// } +// } +//} +// +//func TestTickerRetro1Hour(t *testing.T) { +// offset := time.Duration(10) * time.Second +// last := time.Unix(0, 0) +// mock := clock.NewMock() +// mock.Add(time.Duration(1) * time.Hour) +// desiredLast := mock.Now().Add(-offset) +// ticker := NewTicker(last, offset, mock) +// +// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t) +// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) +// +//} +// +//func TestAdvanceWithUpdateOffset(t *testing.T) { +// offset := time.Duration(10) * time.Second +// last := time.Unix(0, 0) +// mock := clock.NewMock() +// mock.Add(time.Duration(1) * time.Hour) +// desiredLast := mock.Now().Add(-offset) +// ticker := NewTicker(last, offset, mock) +// +// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t) +// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) +// +// // lowering offset should see a few more ticks +// offset = time.Duration(5) * time.Second +// ticker.updateOffset(offset) +// desiredLast = mock.Now().Add(-offset) +// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(9)*time.Millisecond, t) +// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) +// +// // advancing clock should see even more ticks +// mock.Add(time.Duration(1) * time.Hour) +// desiredLast = mock.Now().Add(-offset) +// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(8)*time.Millisecond, t) +// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) +// +//} +// +//func getCase(lastSeconds, offsetSeconds int) (time.Time, time.Duration) { +// last := time.Unix(int64(lastSeconds), 0) +// offset := time.Duration(offsetSeconds) * time.Second +// return last, offset +//} +// +//func TestTickerNoAdvance(t *testing.T) { +// +// // it's 00:01:00 now. what are some cases where we don't want the ticker to advance? +// mock := clock.NewMock() +// mock.Add(time.Duration(60) * time.Second) +// +// type Case struct { +// last int +// offset int +// } +// +// // note that some cases add up to now, others go into the future +// cases := []Case{ +// {50, 10}, +// {50, 30}, +// {59, 1}, +// {59, 10}, +// {59, 30}, +// {60, 1}, +// {60, 10}, +// {60, 30}, +// {90, 1}, +// {90, 10}, +// {90, 30}, +// } +// for _, c := range cases { +// last, offset := getCase(c.last, c.offset) +// ticker := NewTicker(last, offset, mock) +// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t) +// } +//} From 816c4d2340c257b473be6528a017ebfaaf22a2ca Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 31 Jan 2018 09:56:22 +0100 Subject: [PATCH 09/17] url: fix for boolean querystring parameters Without this fix then the querystring looks like ?abool=true which causes a mismatch with the angular routing. This results in a redirect and messes up the browser history and back button. --- public/app/core/utils/url.ts | 6 +++++- public/app/stores/ViewStore/ViewStore.jest.ts | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/public/app/core/utils/url.ts b/public/app/core/utils/url.ts index 0b629768b92..b57d5721d57 100644 --- a/public/app/core/utils/url.ts +++ b/public/app/core/utils/url.ts @@ -12,7 +12,11 @@ export function toUrlParams(a) { let add = function(k, v) { v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v; - s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v); + if (typeof v !== 'boolean') { + s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v); + } else { + s[s.length] = encodeURIComponent(k); + } }; let buildParams = function(prefix, obj) { diff --git a/public/app/stores/ViewStore/ViewStore.jest.ts b/public/app/stores/ViewStore/ViewStore.jest.ts index 732f6a81038..b8faed8c0d7 100644 --- a/public/app/stores/ViewStore/ViewStore.jest.ts +++ b/public/app/stores/ViewStore/ViewStore.jest.ts @@ -23,4 +23,10 @@ describe('ViewStore', () => { expect(toJS(store.query.get('values'))).toMatchObject(['A', 'B']); expect(store.currentUrl).toBe('/hello?values=A&values=B'); }); + + it('Query can contain boolean', () => { + store.updatePathAndQuery('/hello', { abool: true }); + expect(toJS(store.query.get('abool'))).toBe(true); + expect(store.currentUrl).toBe('/hello?abool'); + }); }); From 56d5ece340f9440d55c0c4a5c9d575aabd73a1be Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Jan 2018 09:59:07 +0100 Subject: [PATCH 10/17] plugins: return empty tables array insteaf of nil --- pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go index f9c9f9d3b16..332611e339a 100644 --- a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go +++ b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go @@ -71,6 +71,7 @@ func (tw *DatasourcePluginWrapper) Query(ctx context.Context, ds *models.DataSou qr := &tsdb.QueryResult{ RefId: r.RefId, Series: []*tsdb.TimeSeries{}, + Tables: []*tsdb.Table{}, } if r.Error != "" { From 1cfc81de742de1f4bb6ea38a2238ba04b329603e Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 31 Jan 2018 11:06:46 +0100 Subject: [PATCH 11/17] playlist: fixes #10254 Closes #10254 --- public/app/features/playlist/playlist_search.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/playlist/playlist_search.ts b/public/app/features/playlist/playlist_search.ts index 3b80cfdf640..785099ff61c 100644 --- a/public/app/features/playlist/playlist_search.ts +++ b/public/app/features/playlist/playlist_search.ts @@ -12,6 +12,7 @@ export class PlaylistSearchCtrl { $timeout(() => { this.query.query = ''; + this.query.type = 'dash-db'; this.searchDashboards(); }, 100); } From 56c526fad3a3ed87f798dd3b43a47981c82e5cab Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 31 Jan 2018 16:13:47 +0300 Subject: [PATCH 12/17] Repeat panels when row is expanding (#10679) * fix: repeat panels when row is expanding * repeat panel: change test name to more clear one --- .../app/features/dashboard/dashboard_model.ts | 9 ++++ .../features/dashboard/specs/repeat.jest.ts | 51 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index dccd910a85e..4086edc100a 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -569,6 +569,7 @@ export class DashboardModel { if (row.collapsed) { row.collapsed = false; + let hasRepeat = false; if (row.panels.length > 0) { // Use first panel to figure out if it was moved or pushed @@ -589,6 +590,10 @@ export class DashboardModel { // update insert post and y max insertPos += 1; yMax = Math.max(yMax, panel.gridPos.y + panel.gridPos.h); + + if (panel.repeat) { + hasRepeat = true; + } } const pushDownAmount = yMax - row.gridPos.y; @@ -599,6 +604,10 @@ export class DashboardModel { } row.panels = []; + + if (hasRepeat) { + this.processRepeats(); + } } // sort panels diff --git a/public/app/features/dashboard/specs/repeat.jest.ts b/public/app/features/dashboard/specs/repeat.jest.ts index 77b658e7780..93555625b95 100644 --- a/public/app/features/dashboard/specs/repeat.jest.ts +++ b/public/app/features/dashboard/specs/repeat.jest.ts @@ -4,6 +4,57 @@ import { expect } from 'test/lib/common'; jest.mock('app/core/services/context_srv', () => ({})); +describe('given dashboard with panel repeat', function() { + var dashboard; + + beforeEach(function() { + let dashboardJSON = { + panels: [ + { id: 1, type: 'row', gridPos: { x: 0, y: 0, h: 1, w: 24 } }, + { id: 2, repeat: 'apps', repeatDirection: 'h', gridPos: { x: 0, y: 1, h: 2, w: 8 } }, + ], + templating: { + list: [ + { + name: 'apps', + current: { + text: 'se1, se2, se3', + value: ['se1', 'se2', 'se3'], + }, + options: [ + { text: 'se1', value: 'se1', selected: true }, + { text: 'se2', value: 'se2', selected: true }, + { text: 'se3', value: 'se3', selected: true }, + { text: 'se4', value: 'se4', selected: false }, + ], + }, + ], + }, + }; + dashboard = new DashboardModel(dashboardJSON); + dashboard.processRepeats(); + }); + + it('should repeat panels when row is expanding', function() { + expect(dashboard.panels.length).toBe(4); + + // toggle row + dashboard.toggleRow(dashboard.panels[0]); + expect(dashboard.panels.length).toBe(1); + + // change variable + dashboard.templating.list[0].options[2].selected = false; + dashboard.templating.list[0].current = { + text: 'se1, se2', + value: ['se1', 'se2'], + }; + + // toggle row back + dashboard.toggleRow(dashboard.panels[0]); + expect(dashboard.panels.length).toBe(3); + }); +}); + describe('given dashboard with panel repeat in horizontal direction', function() { var dashboard; From ad893614e132427a45eb7253179263ebdab1715f Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 31 Jan 2018 14:14:11 +0100 Subject: [PATCH 13/17] created cta-bg variable and changed bg color on light theme (#10693) --- public/sass/_variables.dark.scss | 1 + public/sass/_variables.light.scss | 1 + public/sass/components/_empty_list_cta.scss | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index b737e7fe6ec..ef9f1a06996 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -135,6 +135,7 @@ $list-item-bg: $card-background; $list-item-hover-bg: lighten($gray-blue, 2%); $list-item-link-color: $text-color; $list-item-shadow: $card-shadow; +$empty-list-cta-bg: $gray-blue; // Scrollbars $scrollbarBackground: #404357; diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index 748c8577f85..a91b4019d1e 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -133,6 +133,7 @@ $list-item-bg: linear-gradient(135deg, $gray-5, $gray-6); //$card-background; $list-item-hover-bg: darken($gray-5, 5%); $list-item-link-color: $text-color; $list-item-shadow: $card-shadow; +$empty-list-cta-bg: $gray-6; // Tables // ------------------------- diff --git a/public/sass/components/_empty_list_cta.scss b/public/sass/components/_empty_list_cta.scss index dd50f3a3b8b..9c33cfb32d5 100644 --- a/public/sass/components/_empty_list_cta.scss +++ b/public/sass/components/_empty_list_cta.scss @@ -1,5 +1,5 @@ .empty-list-cta { - background-color: $search-filter-box-bg; + background-color: $empty-list-cta-bg; text-align: center; padding: $spacer*2; border-radius: $border-radius; From dba93d8240a06ef55697ed472b45e38806fb552a Mon Sep 17 00:00:00 2001 From: Chris Rice Date: Wed, 31 Jan 2018 06:14:34 -0700 Subject: [PATCH 14/17] Renamed "Period" to "Min period" in CloudWatch query editor (#10665) --- .../cloudwatch/partials/query.parameter.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html b/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html index fd14fb7d077..9da0e2d71c4 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html +++ b/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html @@ -1,6 +1,6 @@

    - + @@ -22,7 +22,7 @@
    - +
    @@ -33,9 +33,9 @@
    -
    From f57e106dd59fc15b0b8af61ae13f35c7e7ced171 Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Wed, 31 Jan 2018 14:15:16 +0100 Subject: [PATCH 15/17] docs: Fix outdated provisioning link (#10690) --- docs/sources/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/index.md b/docs/sources/index.md index 3536eb5437e..c1072db47a5 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -18,7 +18,7 @@ other domains including industrial sensors, home automation, weather, and proces - [Installing on Mac OS X](installation/mac) - [Installing on Windows](installation/windows) - [Installing on Docker](installation/docker) -- [Installing using Provisioning (Chef, Puppet, Salt, Ansible, etc)](installation/provisioning) +- [Installing using Provisioning (Chef, Puppet, Salt, Ansible, etc)](administration/provisioning#configuration-management-tools) - [Nightly Builds](https://grafana.com/grafana/download) For other platforms Read the [build from source]({{< relref "project/building_from_source.md" >}}) From d90dfcc2039b458c6ddcd8294f3c37657e6c2147 Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Wed, 31 Jan 2018 14:15:32 +0100 Subject: [PATCH 16/17] docs: Remove obsolete Ansible rule (#10689) According to the author, the Ansible role is obsolete: https://github.com/picotrading/ansible-grafana/issues/8#issuecomment-342210822 As the proposed alternative (https://github.com/jtyr/ansible-grafana/) hasn't been updated in the last two years either, I'm suggesting it should be removed altogether in favor of the role maintained by cloudalchemy. --- docs/sources/administration/provisioning.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index 0750131d42f..1dbaaa2d46f 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -66,7 +66,6 @@ Tool | Project -----|------------ Puppet | [https://forge.puppet.com/puppet/grafana](https://forge.puppet.com/puppet/grafana) Ansible | [https://github.com/cloudalchemy/ansible-grafana](https://github.com/cloudalchemy/ansible-grafana) -Ansible | [https://github.com/picotrading/ansible-grafana](https://github.com/picotrading/ansible-grafana) Chef | [https://github.com/JonathanTron/chef-grafana](https://github.com/JonathanTron/chef-grafana) Saltstack | [https://github.com/salt-formulas/salt-formula-grafana](https://github.com/salt-formulas/salt-formula-grafana) From 4ac21d2e8f4b62e7acbb24392780f70dd4c8e63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 31 Jan 2018 16:20:14 +0100 Subject: [PATCH 17/17] docs: added redirect from old provision page, #10691 --- docs/sources/administration/provisioning.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index 1dbaaa2d46f..c3595969281 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -3,6 +3,7 @@ title = "Provisioning" description = "" keywords = ["grafana", "provisioning"] type = "docs" +aliases = ["/installation/provisioning"] [menu.docs] parent = "admin" weight = 8