dashboard_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. package api
  2. import (
  3. "encoding/json"
  4. "path/filepath"
  5. "testing"
  6. macaron "gopkg.in/macaron.v1"
  7. "github.com/go-macaron/session"
  8. "github.com/grafana/grafana/pkg/api/dtos"
  9. "github.com/grafana/grafana/pkg/bus"
  10. "github.com/grafana/grafana/pkg/components/simplejson"
  11. "github.com/grafana/grafana/pkg/middleware"
  12. m "github.com/grafana/grafana/pkg/models"
  13. "github.com/grafana/grafana/pkg/services/alerting"
  14. "github.com/grafana/grafana/pkg/services/dashboards"
  15. . "github.com/smartystreets/goconvey/convey"
  16. )
  17. type fakeDashboardRepo struct {
  18. inserted []*dashboards.SaveDashboardItem
  19. getDashboard []*m.Dashboard
  20. }
  21. func (repo *fakeDashboardRepo) SaveDashboard(json *dashboards.SaveDashboardItem) (*m.Dashboard, error) {
  22. repo.inserted = append(repo.inserted, json)
  23. return json.Dashboard, nil
  24. }
  25. var fakeRepo *fakeDashboardRepo
  26. func TestDashboardApiEndpoint(t *testing.T) {
  27. Convey("Given a dashboard with a parent folder which does not have an acl", t, func() {
  28. fakeDash := m.NewDashboard("Child dash")
  29. fakeDash.Id = 1
  30. fakeDash.FolderId = 1
  31. fakeDash.HasAcl = false
  32. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  33. query.Result = fakeDash
  34. return nil
  35. })
  36. viewerRole := m.ROLE_VIEWER
  37. editorRole := m.ROLE_EDITOR
  38. aclMockResp := []*m.DashboardAclInfoDTO{
  39. {Role: &viewerRole, Permission: m.PERMISSION_VIEW},
  40. {Role: &editorRole, Permission: m.PERMISSION_EDIT},
  41. }
  42. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  43. query.Result = aclMockResp
  44. return nil
  45. })
  46. bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
  47. query.Result = []*m.UserGroup{}
  48. return nil
  49. })
  50. cmd := m.SaveDashboardCommand{
  51. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  52. "folderId": fakeDash.FolderId,
  53. "title": fakeDash.Title,
  54. "id": fakeDash.Id,
  55. }),
  56. }
  57. Convey("When user is an Org Viewer", func() {
  58. role := m.ROLE_VIEWER
  59. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  60. dash := GetDashboardShouldReturn200(sc)
  61. Convey("Should not be able to edit or save dashboard", func() {
  62. So(dash.Meta.CanEdit, ShouldBeFalse)
  63. So(dash.Meta.CanSave, ShouldBeFalse)
  64. So(dash.Meta.CanAdmin, ShouldBeFalse)
  65. })
  66. })
  67. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  68. CallDeleteDashboard(sc)
  69. So(sc.resp.Code, ShouldEqual, 403)
  70. })
  71. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  72. CallGetDashboardVersion(sc)
  73. So(sc.resp.Code, ShouldEqual, 403)
  74. })
  75. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  76. CallGetDashboardVersions(sc)
  77. So(sc.resp.Code, ShouldEqual, 403)
  78. })
  79. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  80. CallPostDashboard(sc)
  81. So(sc.resp.Code, ShouldEqual, 403)
  82. })
  83. })
  84. Convey("When user is an Org Read Only Editor", func() {
  85. role := m.ROLE_READ_ONLY_EDITOR
  86. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  87. dash := GetDashboardShouldReturn200(sc)
  88. Convey("Should be able to view but not save the dashboard", func() {
  89. So(dash.Meta.CanEdit, ShouldBeFalse)
  90. So(dash.Meta.CanSave, ShouldBeFalse)
  91. So(dash.Meta.CanAdmin, ShouldBeFalse)
  92. })
  93. })
  94. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  95. CallDeleteDashboard(sc)
  96. So(sc.resp.Code, ShouldEqual, 403)
  97. })
  98. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  99. CallGetDashboardVersion(sc)
  100. So(sc.resp.Code, ShouldEqual, 403)
  101. })
  102. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  103. CallGetDashboardVersions(sc)
  104. So(sc.resp.Code, ShouldEqual, 403)
  105. })
  106. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  107. CallPostDashboard(sc)
  108. So(sc.resp.Code, ShouldEqual, 403)
  109. })
  110. })
  111. Convey("When user is an Org Editor", func() {
  112. role := m.ROLE_EDITOR
  113. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  114. dash := GetDashboardShouldReturn200(sc)
  115. Convey("Should be able to edit or save dashboard", func() {
  116. So(dash.Meta.CanEdit, ShouldBeTrue)
  117. So(dash.Meta.CanSave, ShouldBeTrue)
  118. So(dash.Meta.CanAdmin, ShouldBeFalse)
  119. })
  120. })
  121. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  122. CallDeleteDashboard(sc)
  123. So(sc.resp.Code, ShouldEqual, 200)
  124. })
  125. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  126. CallGetDashboardVersion(sc)
  127. So(sc.resp.Code, ShouldEqual, 200)
  128. })
  129. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  130. CallGetDashboardVersions(sc)
  131. So(sc.resp.Code, ShouldEqual, 200)
  132. })
  133. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  134. CallPostDashboard(sc)
  135. So(sc.resp.Code, ShouldEqual, 200)
  136. })
  137. Convey("When saving a dashboard folder in another folder", func() {
  138. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  139. query.Result = fakeDash
  140. query.Result.IsFolder = true
  141. return nil
  142. })
  143. invalidCmd := m.SaveDashboardCommand{
  144. FolderId: fakeDash.FolderId,
  145. IsFolder: true,
  146. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  147. "folderId": fakeDash.FolderId,
  148. "title": fakeDash.Title,
  149. }),
  150. }
  151. Convey("Should return an error", func() {
  152. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, invalidCmd, func(sc *scenarioContext) {
  153. CallPostDashboard(sc)
  154. So(sc.resp.Code, ShouldEqual, 400)
  155. })
  156. })
  157. })
  158. })
  159. })
  160. Convey("Given a dashboard with a parent folder which has an acl", t, func() {
  161. fakeDash := m.NewDashboard("Child dash")
  162. fakeDash.Id = 1
  163. fakeDash.FolderId = 1
  164. fakeDash.HasAcl = true
  165. aclMockResp := []*m.DashboardAclInfoDTO{
  166. {
  167. DashboardId: 1,
  168. Permission: m.PERMISSION_EDIT,
  169. UserId: 200,
  170. },
  171. }
  172. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  173. query.Result = aclMockResp
  174. return nil
  175. })
  176. bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
  177. query.Result = fakeDash
  178. return nil
  179. })
  180. bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
  181. query.Result = []*m.UserGroup{}
  182. return nil
  183. })
  184. cmd := m.SaveDashboardCommand{
  185. FolderId: fakeDash.FolderId,
  186. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  187. "id": fakeDash.Id,
  188. "folderId": fakeDash.FolderId,
  189. "title": fakeDash.Title,
  190. }),
  191. }
  192. Convey("When user is an Org Viewer and has no permissions for this dashboard", func() {
  193. role := m.ROLE_VIEWER
  194. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  195. sc.handlerFunc = GetDashboard
  196. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  197. Convey("Should be denied access", func() {
  198. So(sc.resp.Code, ShouldEqual, 403)
  199. })
  200. })
  201. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  202. CallDeleteDashboard(sc)
  203. So(sc.resp.Code, ShouldEqual, 403)
  204. })
  205. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  206. CallGetDashboardVersion(sc)
  207. So(sc.resp.Code, ShouldEqual, 403)
  208. })
  209. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  210. CallGetDashboardVersions(sc)
  211. So(sc.resp.Code, ShouldEqual, 403)
  212. })
  213. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  214. CallPostDashboard(sc)
  215. So(sc.resp.Code, ShouldEqual, 403)
  216. })
  217. })
  218. Convey("When user is an Org Editor and has no permissions for this dashboard", func() {
  219. role := m.ROLE_EDITOR
  220. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  221. sc.handlerFunc = GetDashboard
  222. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  223. Convey("Should be denied access", func() {
  224. So(sc.resp.Code, ShouldEqual, 403)
  225. })
  226. })
  227. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  228. CallDeleteDashboard(sc)
  229. So(sc.resp.Code, ShouldEqual, 403)
  230. })
  231. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  232. CallGetDashboardVersion(sc)
  233. So(sc.resp.Code, ShouldEqual, 403)
  234. })
  235. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  236. CallGetDashboardVersions(sc)
  237. So(sc.resp.Code, ShouldEqual, 403)
  238. })
  239. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  240. CallPostDashboard(sc)
  241. So(sc.resp.Code, ShouldEqual, 403)
  242. })
  243. })
  244. Convey("When user is an Org Viewer but has an edit permission", func() {
  245. role := m.ROLE_VIEWER
  246. mockResult := []*m.DashboardAclInfoDTO{
  247. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_EDIT},
  248. }
  249. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  250. query.Result = mockResult
  251. return nil
  252. })
  253. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  254. dash := GetDashboardShouldReturn200(sc)
  255. Convey("Should be able to get dashboard with edit rights", func() {
  256. So(dash.Meta.CanEdit, ShouldBeTrue)
  257. So(dash.Meta.CanSave, ShouldBeTrue)
  258. So(dash.Meta.CanAdmin, ShouldBeFalse)
  259. })
  260. })
  261. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  262. CallDeleteDashboard(sc)
  263. So(sc.resp.Code, ShouldEqual, 200)
  264. })
  265. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  266. CallGetDashboardVersion(sc)
  267. So(sc.resp.Code, ShouldEqual, 200)
  268. })
  269. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  270. CallGetDashboardVersions(sc)
  271. So(sc.resp.Code, ShouldEqual, 200)
  272. })
  273. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  274. CallPostDashboard(sc)
  275. So(sc.resp.Code, ShouldEqual, 200)
  276. })
  277. })
  278. Convey("When user is an Org Viewer but has an admin permission", func() {
  279. role := m.ROLE_VIEWER
  280. mockResult := []*m.DashboardAclInfoDTO{
  281. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_ADMIN},
  282. }
  283. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  284. query.Result = mockResult
  285. return nil
  286. })
  287. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  288. dash := GetDashboardShouldReturn200(sc)
  289. Convey("Should be able to get dashboard with edit rights", func() {
  290. So(dash.Meta.CanEdit, ShouldBeTrue)
  291. So(dash.Meta.CanSave, ShouldBeTrue)
  292. So(dash.Meta.CanAdmin, ShouldBeTrue)
  293. })
  294. })
  295. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  296. CallDeleteDashboard(sc)
  297. So(sc.resp.Code, ShouldEqual, 200)
  298. })
  299. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  300. CallGetDashboardVersion(sc)
  301. So(sc.resp.Code, ShouldEqual, 200)
  302. })
  303. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  304. CallGetDashboardVersions(sc)
  305. So(sc.resp.Code, ShouldEqual, 200)
  306. })
  307. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  308. CallPostDashboard(sc)
  309. So(sc.resp.Code, ShouldEqual, 200)
  310. })
  311. })
  312. Convey("When user is an Org Editor but has a view permission", func() {
  313. role := m.ROLE_EDITOR
  314. mockResult := []*m.DashboardAclInfoDTO{
  315. {Id: 1, OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
  316. }
  317. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  318. query.Result = mockResult
  319. return nil
  320. })
  321. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  322. dash := GetDashboardShouldReturn200(sc)
  323. Convey("Should not be able to edit or save dashboard", func() {
  324. So(dash.Meta.CanEdit, ShouldBeFalse)
  325. So(dash.Meta.CanSave, ShouldBeFalse)
  326. })
  327. })
  328. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/2", "/api/dashboards/:id", role, func(sc *scenarioContext) {
  329. CallDeleteDashboard(sc)
  330. So(sc.resp.Code, ShouldEqual, 403)
  331. })
  332. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
  333. CallGetDashboardVersion(sc)
  334. So(sc.resp.Code, ShouldEqual, 403)
  335. })
  336. loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
  337. CallGetDashboardVersions(sc)
  338. So(sc.resp.Code, ShouldEqual, 403)
  339. })
  340. postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
  341. CallPostDashboard(sc)
  342. So(sc.resp.Code, ShouldEqual, 403)
  343. })
  344. })
  345. })
  346. }
  347. func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
  348. sc.handlerFunc = GetDashboard
  349. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  350. So(sc.resp.Code, ShouldEqual, 200)
  351. dash := dtos.DashboardFullWithMeta{}
  352. err := json.NewDecoder(sc.resp.Body).Decode(&dash)
  353. So(err, ShouldBeNil)
  354. return dash
  355. }
  356. func CallGetDashboardVersion(sc *scenarioContext) {
  357. bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
  358. query.Result = &m.DashboardVersion{}
  359. return nil
  360. })
  361. sc.handlerFunc = GetDashboardVersion
  362. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  363. }
  364. func CallGetDashboardVersions(sc *scenarioContext) {
  365. bus.AddHandler("test", func(query *m.GetDashboardVersionsQuery) error {
  366. query.Result = []*m.DashboardVersionDTO{}
  367. return nil
  368. })
  369. sc.handlerFunc = GetDashboardVersions
  370. sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
  371. }
  372. func CallDeleteDashboard(sc *scenarioContext) {
  373. bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
  374. return nil
  375. })
  376. sc.handlerFunc = DeleteDashboard
  377. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  378. }
  379. func CallPostDashboard(sc *scenarioContext) {
  380. bus.AddHandler("test", func(cmd *alerting.ValidateDashboardAlertsCommand) error {
  381. return nil
  382. })
  383. bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
  384. cmd.Result = &m.Dashboard{Id: 2, Slug: "Dash", Version: 2}
  385. return nil
  386. })
  387. bus.AddHandler("test", func(cmd *alerting.UpdateDashboardAlertsCommand) error {
  388. return nil
  389. })
  390. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  391. }
  392. func postDashboardScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.SaveDashboardCommand, fn scenarioFunc) {
  393. Convey(desc+" "+url, func() {
  394. defer bus.ClearBusHandlers()
  395. sc := &scenarioContext{
  396. url: url,
  397. }
  398. viewsPath, _ := filepath.Abs("../../public/views")
  399. sc.m = macaron.New()
  400. sc.m.Use(macaron.Renderer(macaron.RenderOptions{
  401. Directory: viewsPath,
  402. Delims: macaron.Delims{Left: "[[", Right: "]]"},
  403. }))
  404. sc.m.Use(middleware.GetContextHandler())
  405. sc.m.Use(middleware.Sessioner(&session.Options{}))
  406. sc.defaultHandler = wrap(func(c *middleware.Context) Response {
  407. sc.context = c
  408. sc.context.UserId = TestUserID
  409. sc.context.OrgId = TestOrgID
  410. sc.context.OrgRole = role
  411. return PostDashboard(c, cmd)
  412. })
  413. fakeRepo = &fakeDashboardRepo{}
  414. dashboards.SetRepository(fakeRepo)
  415. sc.m.Post(routePattern, sc.defaultHandler)
  416. fn(sc)
  417. })
  418. }