From 369597f7b2b8093bf5509a8d85012aef9cc1772f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 30 Jan 2018 23:37:54 +0100 Subject: [PATCH 1/9] dashboards: return url in response to save dashboard. #7883 --- pkg/api/dashboard.go | 16 +++++++++++++++- pkg/api/dashboard_test.go | 36 +++++++++++++++--------------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index a1c9950c457..7799ad868f9 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -238,8 +238,22 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response { return ApiError(500, "Invalid alert data. Cannot save dashboard", err) } + var url string + if dash.IsFolder { + url = m.GetFolderUrl(dashboard.Uid, dashboard.Slug) + } else { + url = m.GetDashboardUrl(dashboard.Uid, dashboard.Slug) + } + c.TimeRequest(metrics.M_Api_Dashboard_Save) - return Json(200, util.DynMap{"status": "success", "slug": dashboard.Slug, "version": dashboard.Version, "id": dashboard.Id, "uid": dashboard.Uid}) + return Json(200, util.DynMap{ + "status": "success", + "slug": dashboard.Slug, + "version": dashboard.Version, + "id": dashboard.Id, + "uid": dashboard.Uid, + "url": url, + }) } func GetHomeDashboard(c *middleware.Context) Response { diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index cc718229f96..0bd586f6067 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -180,13 +180,7 @@ func TestDashboardApiEndpoint(t *testing.T) { }) postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) { - CallPostDashboard(sc) - So(sc.resp.Code, ShouldEqual, 200) - result := sc.ToJson() - So(result.Get("status").MustString(), ShouldEqual, "success") - So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0) - So(result.Get("uid").MustString(), ShouldNotBeNil) - So(result.Get("slug").MustString(), ShouldNotBeNil) + CallPostDashboardShouldReturnSuccess(sc) }) Convey("When saving a dashboard folder in another folder", func() { @@ -423,13 +417,7 @@ func TestDashboardApiEndpoint(t *testing.T) { }) postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) { - CallPostDashboard(sc) - So(sc.resp.Code, ShouldEqual, 200) - result := sc.ToJson() - So(result.Get("status").MustString(), ShouldEqual, "success") - So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0) - So(result.Get("uid").MustString(), ShouldNotBeNil) - So(result.Get("slug").MustString(), ShouldNotBeNil) + CallPostDashboardShouldReturnSuccess(sc) }) }) @@ -544,13 +532,7 @@ func TestDashboardApiEndpoint(t *testing.T) { }) postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) { - CallPostDashboard(sc) - So(sc.resp.Code, ShouldEqual, 200) - result := sc.ToJson() - So(result.Get("status").MustString(), ShouldEqual, "success") - So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0) - So(result.Get("uid").MustString(), ShouldNotBeNil) - So(result.Get("slug").MustString(), ShouldNotBeNil) + CallPostDashboardShouldReturnSuccess(sc) }) }) @@ -678,6 +660,18 @@ func CallPostDashboard(sc *scenarioContext) { sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() } +func CallPostDashboardShouldReturnSuccess(sc *scenarioContext) { + CallPostDashboard(sc) + + So(sc.resp.Code, ShouldEqual, 200) + result := sc.ToJson() + So(result.Get("status").MustString(), ShouldEqual, "success") + So(result.Get("id").MustInt64(), ShouldBeGreaterThan, 0) + So(result.Get("uid").MustString(), ShouldNotBeNil) + So(result.Get("slug").MustString(), ShouldNotBeNil) + So(result.Get("url").MustString(), ShouldNotBeNil) +} + func postDashboardScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.SaveDashboardCommand, fn scenarioFunc) { Convey(desc+" "+url, func() { defer bus.ClearBusHandlers() From aefcff26a61f026958ee3ffe58f13b2b07ea43cd Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 00:26:26 +0100 Subject: [PATCH 2/9] dashboards: add new default frontend route for loading a dashboard New default route /d// where dashboard are loaded by unique identifier. If old route /dashboard/db/ are used, try to lookup dashboard by slug and redirect to new default route. #7883 --- public/app/core/services/backend_srv.ts | 4 ++++ .../app/features/dashboard/dashboard_loader_srv.ts | 6 +++--- public/app/routes/dashboard_loaders.ts | 14 ++++++++++++-- public/app/routes/routes.ts | 6 ++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 3204db0ce5d..9ade4264bb3 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -225,6 +225,10 @@ export class BackendSrv { return this.get('/api/dashboards/' + type + '/' + slug); } + getDashboardByUid(uid: string) { + return this.get(`/api/dashboards/uid/${uid}`); + } + saveDashboard(dash, options) { options = options || {}; diff --git a/public/app/features/dashboard/dashboard_loader_srv.ts b/public/app/features/dashboard/dashboard_loader_srv.ts index e4def4385e1..9e458c4e4bb 100644 --- a/public/app/features/dashboard/dashboard_loader_srv.ts +++ b/public/app/features/dashboard/dashboard_loader_srv.ts @@ -35,18 +35,18 @@ export class DashboardLoaderSrv { }; } - loadDashboard(type, slug) { + loadDashboard(type, slug, uid) { var promise; if (type === 'script') { promise = this._loadScriptedDashboard(slug); } else if (type === 'snapshot') { - promise = this.backendSrv.get('/api/snapshots/' + this.$routeParams.slug).catch(() => { + promise = this.backendSrv.get('/api/snapshots/' + slug).catch(() => { return this._dashboardLoadFailed('Snapshot not found', true); }); } else { promise = this.backendSrv - .getDashboard(this.$routeParams.type, this.$routeParams.slug) + .getDashboardByUid(uid) .then(result => { if (result.meta.isFolder) { this.$rootScope.appEvent('alert-error', ['Dashboard not found']); diff --git a/public/app/routes/dashboard_loaders.ts b/public/app/routes/dashboard_loaders.ts index f4f0b36ac72..e3e6f4c18bc 100644 --- a/public/app/routes/dashboard_loaders.ts +++ b/public/app/routes/dashboard_loaders.ts @@ -5,7 +5,7 @@ export class LoadDashboardCtrl { constructor($scope, $routeParams, dashboardLoaderSrv, backendSrv, $location) { $scope.appEvent('dashboard-fetch-start'); - if (!$routeParams.slug) { + if (!$routeParams.uid && !$routeParams.slug) { backendSrv.get('/api/dashboards/home').then(function(homeDash) { if (homeDash.redirectUri) { $location.path('dashboard/' + homeDash.redirectUri); @@ -18,7 +18,17 @@ export class LoadDashboardCtrl { return; } - dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { + // if no uid, redirect to new route based on slug + if (!$routeParams.uid) { + backendSrv.get(`/api/dashboards/db/${$routeParams.slug}`).then(res => { + if (res) { + $location.path(res.meta.url); + } + }); + return; + } + + dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug, $routeParams.uid).then(function(result) { if ($routeParams.keepRows) { result.meta.keepRows = true; } diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index ec65e4ec25a..081fb88af6e 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -14,6 +14,12 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { reloadOnSearch: false, pageClass: 'page-dashboard', }) + .when('/d/:uid/:slug', { + templateUrl: 'public/app/partials/dashboard.html', + controller: 'LoadDashboardCtrl', + reloadOnSearch: false, + pageClass: 'page-dashboard', + }) .when('/dashboard/:type/:slug', { templateUrl: 'public/app/partials/dashboard.html', controller: 'LoadDashboardCtrl', From 8009307c16732e39eb83e482cc720f3897d29b9b Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 00:32:51 +0100 Subject: [PATCH 3/9] dashboards: when saving dashboard redirect if url changes Redirect if new dashboard created or existing url changes. #7883 --- public/app/features/dashboard/dashboard_srv.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/public/app/features/dashboard/dashboard_srv.ts b/public/app/features/dashboard/dashboard_srv.ts index 708ba047d2b..07fc14f2f2e 100644 --- a/public/app/features/dashboard/dashboard_srv.ts +++ b/public/app/features/dashboard/dashboard_srv.ts @@ -73,9 +73,8 @@ export class DashboardSrv { postSave(clone, data) { this.dash.version = data.version; - var dashboardUrl = '/dashboard/db/' + data.slug; - if (dashboardUrl !== this.$location.path()) { - this.$location.url(dashboardUrl); + if (data.url !== this.$location.path()) { + this.$location.url(data.url); } this.$rootScope.appEvent('dashboard-saved', this.dash); From f2014223b490911b699a723ba3e8f1b58e9affa4 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 00:42:17 +0100 Subject: [PATCH 4/9] dashboards: use new *url* prop from dashboard search for linking to dashboards #7883 --- public/app/core/services/search_srv.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/public/app/core/services/search_srv.ts b/public/app/core/services/search_srv.ts index a909b4af09f..a0989e74aed 100644 --- a/public/app/core/services/search_srv.ts +++ b/public/app/core/services/search_srv.ts @@ -41,10 +41,7 @@ export class SearchSrv { .map(orderId => { return _.find(result, { id: orderId }); }) - .filter(hit => hit && !hit.isStarred) - .map(hit => { - return this.transformToViewModel(hit); - }); + .filter(hit => hit && !hit.isStarred); }); } @@ -81,17 +78,12 @@ export class SearchSrv { score: -2, expanded: this.starredIsOpen, toggle: this.toggleStarred.bind(this), - items: result.map(this.transformToViewModel), + items: result, }; } }); } - private transformToViewModel(hit) { - hit.url = 'dashboard/db/' + hit.slug; - return hit; - } - search(options) { let sections: any = {}; let promises = []; @@ -181,7 +173,7 @@ export class SearchSrv { } section.expanded = true; - section.items.push(this.transformToViewModel(hit)); + section.items.push(hit); } } @@ -198,7 +190,7 @@ export class SearchSrv { }; return this.backendSrv.search(query).then(results => { - section.items = _.map(results, this.transformToViewModel); + section.items = results; return Promise.resolve(section); }); } From 7734df1d1859d7aed6a8c04d0a3131eaa1cd7e86 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 00:48:36 +0100 Subject: [PATCH 5/9] dashboards: fix links to recently viewed and starred dashboards Use new property url from dashboard search for linking to dashboards #7883 --- public/app/plugins/panel/dashlist/module.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/dashlist/module.html b/public/app/plugins/panel/dashlist/module.html index 7f71811ac08..8fa3e7ef71f 100644 --- a/public/app/plugins/panel/dashlist/module.html +++ b/public/app/plugins/panel/dashlist/module.html @@ -4,7 +4,7 @@ {{group.header}}
- + {{dash.title}} From 3efb3d8efa11e05d8e629f1043f657ecd89d0572 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 11:01:01 +0100 Subject: [PATCH 6/9] dashboards: add new default frontend route for rendering a dashboard panel New default route /d-solo// where dashboard panel are rendered by unique identifier and panel identifier. If old route /dashboard-solo/db/ are used, try to lookup dashboard by slug and redirect to new default route. Change url logic for sharing a panel so that the new default route for rendering a dashboard panel are used. #7883 --- public/app/features/dashboard/shareModalCtrl.ts | 7 ++----- .../dashboard/specs/share_modal_ctrl_specs.ts | 4 ++-- public/app/features/panel/solo_panel_ctrl.ts | 14 ++++++++++++-- public/app/routes/routes.ts | 6 ++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/public/app/features/dashboard/shareModalCtrl.ts b/public/app/features/dashboard/shareModalCtrl.ts index 508658cd53b..2bde5c2d198 100644 --- a/public/app/features/dashboard/shareModalCtrl.ts +++ b/public/app/features/dashboard/shareModalCtrl.ts @@ -73,17 +73,14 @@ export class ShareModalCtrl { $scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params); - var soloUrl = baseUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/'); + var soloUrl = baseUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/'); delete params.fullscreen; delete params.edit; soloUrl = linkSrv.addParamsToUrl(soloUrl, params); $scope.iframeHtml = ''; - $scope.imageUrl = soloUrl.replace( - config.appSubUrl + '/dashboard-solo/', - config.appSubUrl + '/render/dashboard-solo/' - ); + $scope.imageUrl = soloUrl.replace(config.appSubUrl + '/d-solo/', config.appSubUrl + '/render/d-solo/'); $scope.imageUrl += '&width=1000'; $scope.imageUrl += '&height=500'; $scope.imageUrl += '&tz=UTC' + encodeURIComponent(moment().format('Z')); diff --git a/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts b/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts index 8bf8f53ed46..e65d9c98f82 100644 --- a/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts +++ b/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts @@ -43,12 +43,12 @@ describe('ShareModalCtrl', function() { }); it('should generate render url', function() { - ctx.$location.$$absUrl = 'http://dashboards.grafana.com/dashboard/db/my-dash'; + ctx.$location.$$absUrl = 'http://dashboards.grafana.com/d/abcdefghi/my-dash'; ctx.scope.panel = { id: 22 }; ctx.scope.init(); - var base = 'http://dashboards.grafana.com/render/dashboard-solo/db/my-dash'; + var base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash'; var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC'; expect(ctx.scope.imageUrl).to.contain(base + params); }); diff --git a/public/app/features/panel/solo_panel_ctrl.ts b/public/app/features/panel/solo_panel_ctrl.ts index f141f89eb80..d8642bea4a0 100644 --- a/public/app/features/panel/solo_panel_ctrl.ts +++ b/public/app/features/panel/solo_panel_ctrl.ts @@ -2,7 +2,7 @@ import angular from 'angular'; export class SoloPanelCtrl { /** @ngInject */ - constructor($scope, $routeParams, $location, dashboardLoaderSrv, contextSrv) { + constructor($scope, $routeParams, $location, dashboardLoaderSrv, contextSrv, backendSrv) { var panelId; $scope.init = function() { @@ -13,7 +13,17 @@ export class SoloPanelCtrl { $scope.onAppEvent('dashboard-initialized', $scope.initPanelScope); - dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { + // if no uid, redirect to new route based on slug + if (!$routeParams.uid) { + backendSrv.get(`/api/dashboards/db/${$routeParams.slug}`).then(res => { + if (res) { + $location.path(res.meta.url.replace('/d/', '/d-solo/')); + } + }); + return; + } + + dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug, $routeParams.uid).then(function(result) { result.meta.soloMode = true; $scope.initDashboard(result, $scope); }); diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index 081fb88af6e..865f4ce1682 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -26,6 +26,12 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { reloadOnSearch: false, pageClass: 'page-dashboard', }) + .when('/d-solo/:uid/:slug', { + templateUrl: 'public/app/features/panel/partials/soloPanel.html', + controller: 'SoloPanelCtrl', + reloadOnSearch: false, + pageClass: 'page-dashboard', + }) .when('/dashboard-solo/:type/:slug', { templateUrl: 'public/app/features/panel/partials/soloPanel.html', controller: 'SoloPanelCtrl', From a99331cdb94c12d43d7d385a35945c8163294e61 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 14:05:24 +0100 Subject: [PATCH 7/9] dashboards: redirect from old url used to load dashboard to new url If legacy backend routes (/dashboard/db/ and /dashboard-solo/db/) are requested we try to redirect to new routes with a 301 Moved Permanently #7883 --- pkg/api/api.go | 8 +++- pkg/middleware/dashboard_redirect.go | 46 +++++++++++++++++++ pkg/middleware/dashboard_redirect_test.go | 54 +++++++++++++++++++++++ pkg/middleware/middleware_test.go | 14 ++++++ 4 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 pkg/middleware/dashboard_redirect.go create mode 100644 pkg/middleware/dashboard_redirect_test.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 2d45868e58d..18e18bf5d54 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -15,6 +15,8 @@ func (hs *HttpServer) registerRoutes() { reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true}) reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN) reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN) + redirectFromLegacyDashboardUrl := middleware.RedirectFromLegacyDashboardUrl() + redirectFromLegacyDashboardSoloUrl := middleware.RedirectFromLegacyDashboardSoloUrl() quota := middleware.Quota bind := binding.Bind @@ -63,9 +65,11 @@ func (hs *HttpServer) registerRoutes() { r.Get("/plugins/:id/edit", reqSignedIn, Index) r.Get("/plugins/:id/page/:page", reqSignedIn, Index) - r.Get("/dashboard/*", reqSignedIn, Index) + r.Get("/d/:uid/:slug", reqSignedIn, Index) + r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardUrl, Index) r.Get("/dashboard-solo/snapshot/*", Index) - r.Get("/dashboard-solo/*", reqSignedIn, Index) + r.Get("/d-solo/:uid/:slug", reqSignedIn, Index) + r.Get("/dashboard-solo/db/:slug", reqSignedIn, redirectFromLegacyDashboardSoloUrl, Index) r.Get("/import/dashboard", reqSignedIn, Index) r.Get("/dashboards/", reqSignedIn, Index) r.Get("/dashboards/*", reqSignedIn, Index) diff --git a/pkg/middleware/dashboard_redirect.go b/pkg/middleware/dashboard_redirect.go new file mode 100644 index 00000000000..1ca4ef741c6 --- /dev/null +++ b/pkg/middleware/dashboard_redirect.go @@ -0,0 +1,46 @@ +package middleware + +import ( + "strings" + + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" + "gopkg.in/macaron.v1" +) + +func getDashboardUrlBySlug(orgId int64, slug string) (string, error) { + query := m.GetDashboardQuery{Slug: slug, OrgId: orgId} + + if err := bus.Dispatch(&query); err != nil { + return "", m.ErrDashboardNotFound + } + + return m.GetDashboardUrl(query.Result.Uid, query.Result.Slug), nil +} + +func RedirectFromLegacyDashboardUrl() macaron.Handler { + return func(c *Context) { + slug := c.Params("slug") + + if slug != "" { + if url, err := getDashboardUrlBySlug(c.OrgId, slug); err == nil { + c.Redirect(url, 301) + return + } + } + } +} + +func RedirectFromLegacyDashboardSoloUrl() macaron.Handler { + return func(c *Context) { + slug := c.Params("slug") + + if slug != "" { + if url, err := getDashboardUrlBySlug(c.OrgId, slug); err == nil { + url = strings.Replace(url, "/d/", "/d-solo/", 1) + c.Redirect(url, 301) + return + } + } + } +} diff --git a/pkg/middleware/dashboard_redirect_test.go b/pkg/middleware/dashboard_redirect_test.go new file mode 100644 index 00000000000..bff4ee2253c --- /dev/null +++ b/pkg/middleware/dashboard_redirect_test.go @@ -0,0 +1,54 @@ +package middleware + +import ( + "strings" + "testing" + + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" + . "github.com/smartystreets/goconvey/convey" +) + +func TestMiddlewareDashboardRedirect(t *testing.T) { + Convey("Given the dashboard redirect middleware", t, func() { + bus.ClearBusHandlers() + redirectFromLegacyDashboardUrl := RedirectFromLegacyDashboardUrl() + redirectFromLegacyDashboardSoloUrl := RedirectFromLegacyDashboardSoloUrl() + + fakeDash := m.NewDashboard("Child dash") + fakeDash.Id = 1 + fakeDash.FolderId = 1 + fakeDash.HasAcl = false + + bus.AddHandler("test", func(query *m.GetDashboardQuery) error { + query.Result = fakeDash + return nil + }) + + middlewareScenario("GET dashboard by legacy url", func(sc *scenarioContext) { + sc.m.Get("/dashboard/db/:slug", redirectFromLegacyDashboardUrl, sc.defaultHandler) + + sc.fakeReqWithParams("GET", "/dashboard/db/dash", map[string]string{}).exec() + + Convey("Should redirect to new dashboard url with a 301 Moved Permanently", func() { + So(sc.resp.Code, ShouldEqual, 301) + redirectUrl, _ := sc.resp.Result().Location() + So(redirectUrl.Path, ShouldEqual, m.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug)) + }) + }) + + middlewareScenario("GET dashboard solo by legacy url", func(sc *scenarioContext) { + sc.m.Get("/dashboard-solo/db/:slug", redirectFromLegacyDashboardSoloUrl, sc.defaultHandler) + + sc.fakeReqWithParams("GET", "/dashboard-solo/db/dash", map[string]string{}).exec() + + Convey("Should redirect to new dashboard url with a 301 Moved Permanently", func() { + So(sc.resp.Code, ShouldEqual, 301) + redirectUrl, _ := sc.resp.Result().Location() + expectedUrl := m.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug) + expectedUrl = strings.Replace(expectedUrl, "/d/", "/d-solo/", 1) + So(redirectUrl.Path, ShouldEqual, expectedUrl) + }) + }) + }) +} diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 0d9e0e5b973..ffd8e8a0af0 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -399,6 +399,20 @@ func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext { return sc } +func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map[string]string) *scenarioContext { + sc.resp = httptest.NewRecorder() + req, err := http.NewRequest(method, url, nil) + q := req.URL.Query() + for k, v := range queryParams { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + So(err, ShouldBeNil) + sc.req = req + + return sc +} + func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext { sc.handlerFunc = fn return sc From 57edf890338e93f70f8c2fe3176d7702d7c90f1c Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 14:07:49 +0100 Subject: [PATCH 8/9] dashboards: make scripted dashboards work using the old legacy urls Scripted dashboards are still requested from /dashboard/script/scripted.js #7883 --- pkg/api/api.go | 2 ++ public/app/features/dashboard/shareModalCtrl.ts | 9 +++++++-- .../dashboard/specs/share_modal_ctrl_specs.ts | 11 +++++++++++ public/app/features/panel/solo_panel_ctrl.ts | 2 +- public/app/routes/dashboard_loaders.ts | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 18e18bf5d54..6d2207971e7 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -67,9 +67,11 @@ func (hs *HttpServer) registerRoutes() { r.Get("/d/:uid/:slug", reqSignedIn, Index) r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardUrl, Index) + r.Get("/dashboard/script/*", reqSignedIn, Index) r.Get("/dashboard-solo/snapshot/*", Index) r.Get("/d-solo/:uid/:slug", reqSignedIn, Index) r.Get("/dashboard-solo/db/:slug", reqSignedIn, redirectFromLegacyDashboardSoloUrl, Index) + r.Get("/dashboard-solo/script/*", reqSignedIn, Index) r.Get("/import/dashboard", reqSignedIn, Index) r.Get("/dashboards/", reqSignedIn, Index) r.Get("/dashboards/*", reqSignedIn, Index) diff --git a/public/app/features/dashboard/shareModalCtrl.ts b/public/app/features/dashboard/shareModalCtrl.ts index 2bde5c2d198..8e30eaef91e 100644 --- a/public/app/features/dashboard/shareModalCtrl.ts +++ b/public/app/features/dashboard/shareModalCtrl.ts @@ -73,14 +73,19 @@ export class ShareModalCtrl { $scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params); - var soloUrl = baseUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/'); + var soloUrl = baseUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/'); + soloUrl = soloUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/'); delete params.fullscreen; delete params.edit; soloUrl = linkSrv.addParamsToUrl(soloUrl, params); $scope.iframeHtml = ''; - $scope.imageUrl = soloUrl.replace(config.appSubUrl + '/d-solo/', config.appSubUrl + '/render/d-solo/'); + $scope.imageUrl = soloUrl.replace( + config.appSubUrl + '/dashboard-solo/', + config.appSubUrl + '/render/dashboard-solo/' + ); + $scope.imageUrl = $scope.imageUrl.replace(config.appSubUrl + '/d-solo/', config.appSubUrl + '/render/d-solo/'); $scope.imageUrl += '&width=1000'; $scope.imageUrl += '&height=500'; $scope.imageUrl += '&tz=UTC' + encodeURIComponent(moment().format('Z')); diff --git a/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts b/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts index e65d9c98f82..fc70a54a41c 100644 --- a/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts +++ b/public/app/features/dashboard/specs/share_modal_ctrl_specs.ts @@ -53,6 +53,17 @@ describe('ShareModalCtrl', function() { expect(ctx.scope.imageUrl).to.contain(base + params); }); + it('should generate render url for scripted dashboard', function() { + ctx.$location.$$absUrl = 'http://dashboards.grafana.com/dashboard/script/my-dash.js'; + + ctx.scope.panel = { id: 22 }; + + ctx.scope.init(); + var base = 'http://dashboards.grafana.com/render/dashboard-solo/script/my-dash.js'; + var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC'; + expect(ctx.scope.imageUrl).to.contain(base + params); + }); + it('should remove panel id when no panel in scope', function() { ctx.$location.path('/test'); ctx.scope.options.forCurrent = true; diff --git a/public/app/features/panel/solo_panel_ctrl.ts b/public/app/features/panel/solo_panel_ctrl.ts index d8642bea4a0..575c9e4c3b9 100644 --- a/public/app/features/panel/solo_panel_ctrl.ts +++ b/public/app/features/panel/solo_panel_ctrl.ts @@ -14,7 +14,7 @@ export class SoloPanelCtrl { $scope.onAppEvent('dashboard-initialized', $scope.initPanelScope); // if no uid, redirect to new route based on slug - if (!$routeParams.uid) { + if (!($routeParams.type === 'script' || $routeParams.type === 'snapshot') && !$routeParams.uid) { backendSrv.get(`/api/dashboards/db/${$routeParams.slug}`).then(res => { if (res) { $location.path(res.meta.url.replace('/d/', '/d-solo/')); diff --git a/public/app/routes/dashboard_loaders.ts b/public/app/routes/dashboard_loaders.ts index e3e6f4c18bc..971c83f0414 100644 --- a/public/app/routes/dashboard_loaders.ts +++ b/public/app/routes/dashboard_loaders.ts @@ -19,7 +19,7 @@ export class LoadDashboardCtrl { } // if no uid, redirect to new route based on slug - if (!$routeParams.uid) { + if (!($routeParams.type === 'script' || $routeParams.type === 'snapshot') && !$routeParams.uid) { backendSrv.get(`/api/dashboards/db/${$routeParams.slug}`).then(res => { if (res) { $location.path(res.meta.url); From 75cf9ae27de2887b8e0149814463e0b9053e0a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 31 Jan 2018 17:26:35 +0100 Subject: [PATCH 9/9] fix: use replace when redirecting to new url --- public/app/routes/dashboard_loaders.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/routes/dashboard_loaders.ts b/public/app/routes/dashboard_loaders.ts index 971c83f0414..4ad77512ad1 100644 --- a/public/app/routes/dashboard_loaders.ts +++ b/public/app/routes/dashboard_loaders.ts @@ -22,7 +22,7 @@ export class LoadDashboardCtrl { if (!($routeParams.type === 'script' || $routeParams.type === 'snapshot') && !$routeParams.uid) { backendSrv.get(`/api/dashboards/db/${$routeParams.slug}`).then(res => { if (res) { - $location.path(res.meta.url); + $location.path(res.meta.url).replace(); } }); return;