dashboard_service_integration_test.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. package sqlstore
  2. import (
  3. "testing"
  4. "github.com/grafana/grafana/pkg/components/simplejson"
  5. "github.com/grafana/grafana/pkg/services/dashboards"
  6. "github.com/grafana/grafana/pkg/services/guardian"
  7. "github.com/grafana/grafana/pkg/bus"
  8. "github.com/grafana/grafana/pkg/models"
  9. . "github.com/smartystreets/goconvey/convey"
  10. )
  11. func TestIntegratedDashboardService(t *testing.T) {
  12. Convey("Dashboard service integration tests", t, func() {
  13. InitTestDB(t)
  14. var testOrgId int64 = 1
  15. Convey("Given saved folders and dashboards in organization A", func() {
  16. bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
  17. return nil
  18. })
  19. bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
  20. return nil
  21. })
  22. bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
  23. cmd.Result = nil
  24. return nil
  25. })
  26. savedFolder := saveTestFolder("Saved folder", testOrgId)
  27. savedDashInFolder := saveTestDashboard("Saved dash in folder", testOrgId, savedFolder.Id)
  28. saveTestDashboard("Other saved dash in folder", testOrgId, savedFolder.Id)
  29. savedDashInGeneralFolder := saveTestDashboard("Saved dashboard in general folder", testOrgId, 0)
  30. otherSavedFolder := saveTestFolder("Other saved folder", testOrgId)
  31. Convey("Should return dashboard model", func() {
  32. So(savedFolder.Title, ShouldEqual, "Saved folder")
  33. So(savedFolder.Slug, ShouldEqual, "saved-folder")
  34. So(savedFolder.Id, ShouldNotEqual, 0)
  35. So(savedFolder.IsFolder, ShouldBeTrue)
  36. So(savedFolder.FolderId, ShouldEqual, 0)
  37. So(len(savedFolder.Uid), ShouldBeGreaterThan, 0)
  38. So(savedDashInFolder.Title, ShouldEqual, "Saved dash in folder")
  39. So(savedDashInFolder.Slug, ShouldEqual, "saved-dash-in-folder")
  40. So(savedDashInFolder.Id, ShouldNotEqual, 0)
  41. So(savedDashInFolder.IsFolder, ShouldBeFalse)
  42. So(savedDashInFolder.FolderId, ShouldEqual, savedFolder.Id)
  43. So(len(savedDashInFolder.Uid), ShouldBeGreaterThan, 0)
  44. })
  45. // Basic validation tests
  46. Convey("When saving a dashboard with non-existing id", func() {
  47. cmd := models.SaveDashboardCommand{
  48. OrgId: testOrgId,
  49. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  50. "id": float64(123412321),
  51. "title": "Expect error",
  52. }),
  53. }
  54. err := callSaveWithError(cmd)
  55. Convey("It should result in not found error", func() {
  56. So(err, ShouldNotBeNil)
  57. So(err, ShouldEqual, models.ErrDashboardNotFound)
  58. })
  59. })
  60. // Given other organization
  61. Convey("Given organization B", func() {
  62. var otherOrgId int64 = 2
  63. Convey("When creating a dashboard with same id as dashboard in organization A", func() {
  64. cmd := models.SaveDashboardCommand{
  65. OrgId: otherOrgId,
  66. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  67. "id": savedDashInFolder.Id,
  68. "title": "Expect error",
  69. }),
  70. Overwrite: false,
  71. }
  72. err := callSaveWithError(cmd)
  73. Convey("It should result in not found error", func() {
  74. So(err, ShouldNotBeNil)
  75. So(err, ShouldEqual, models.ErrDashboardNotFound)
  76. })
  77. })
  78. permissionScenario("Given user has permission to save", true, func(sc *dashboardPermissionScenarioContext) {
  79. Convey("When creating a dashboard with same uid as dashboard in organization A", func() {
  80. var otherOrgId int64 = 2
  81. cmd := models.SaveDashboardCommand{
  82. OrgId: otherOrgId,
  83. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  84. "uid": savedDashInFolder.Uid,
  85. "title": "Dash with existing uid in other org",
  86. }),
  87. Overwrite: false,
  88. }
  89. res := callSaveWithResult(cmd)
  90. Convey("It should create a new dashboard in organization B", func() {
  91. So(res, ShouldNotBeNil)
  92. query := models.GetDashboardQuery{OrgId: otherOrgId, Uid: savedDashInFolder.Uid}
  93. err := bus.Dispatch(&query)
  94. So(err, ShouldBeNil)
  95. So(query.Result.Id, ShouldNotEqual, savedDashInFolder.Id)
  96. So(query.Result.Id, ShouldEqual, res.Id)
  97. So(query.Result.OrgId, ShouldEqual, otherOrgId)
  98. So(query.Result.Uid, ShouldEqual, savedDashInFolder.Uid)
  99. })
  100. })
  101. })
  102. })
  103. // Given user has no permission to save
  104. permissionScenario("Given user has no permission to save", false, func(sc *dashboardPermissionScenarioContext) {
  105. Convey("When creating a new dashboard in the General folder", func() {
  106. cmd := models.SaveDashboardCommand{
  107. OrgId: testOrgId,
  108. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  109. "title": "Dash",
  110. }),
  111. UserId: 10000,
  112. Overwrite: true,
  113. }
  114. err := callSaveWithError(cmd)
  115. Convey("It should create dashboard guardian for General Folder with correct arguments and result in access denied error", func() {
  116. So(err, ShouldNotBeNil)
  117. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  118. So(sc.dashboardGuardianMock.DashId, ShouldEqual, 0)
  119. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  120. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  121. })
  122. })
  123. Convey("When creating a new dashboard in other folder", func() {
  124. cmd := models.SaveDashboardCommand{
  125. OrgId: testOrgId,
  126. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  127. "title": "Dash",
  128. }),
  129. FolderId: otherSavedFolder.Id,
  130. UserId: 10000,
  131. Overwrite: true,
  132. }
  133. err := callSaveWithError(cmd)
  134. Convey("It should create dashboard guardian for other folder with correct arguments and rsult in access denied error", func() {
  135. So(err, ShouldNotBeNil)
  136. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  137. So(sc.dashboardGuardianMock.DashId, ShouldEqual, otherSavedFolder.Id)
  138. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  139. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  140. })
  141. })
  142. Convey("When creating a new dashboard by existing title in folder", func() {
  143. cmd := models.SaveDashboardCommand{
  144. OrgId: testOrgId,
  145. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  146. "title": savedDashInFolder.Title,
  147. }),
  148. FolderId: savedFolder.Id,
  149. UserId: 10000,
  150. Overwrite: true,
  151. }
  152. err := callSaveWithError(cmd)
  153. Convey("It should create dashboard guardian for folder with correct arguments and result in access denied error", func() {
  154. So(err, ShouldNotBeNil)
  155. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  156. So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedFolder.Id)
  157. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  158. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  159. })
  160. })
  161. Convey("When creating a new dashboard by existing uid in folder", func() {
  162. cmd := models.SaveDashboardCommand{
  163. OrgId: testOrgId,
  164. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  165. "uid": savedDashInFolder.Uid,
  166. "title": "New dash",
  167. }),
  168. FolderId: savedFolder.Id,
  169. UserId: 10000,
  170. Overwrite: true,
  171. }
  172. err := callSaveWithError(cmd)
  173. Convey("It should create dashboard guardian for folder with correct arguments and result in access denied error", func() {
  174. So(err, ShouldNotBeNil)
  175. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  176. So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedFolder.Id)
  177. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  178. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  179. })
  180. })
  181. Convey("When updating a dashboard by existing id in the General folder", func() {
  182. cmd := models.SaveDashboardCommand{
  183. OrgId: testOrgId,
  184. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  185. "id": savedDashInGeneralFolder.Id,
  186. "title": "Dash",
  187. }),
  188. FolderId: savedDashInGeneralFolder.FolderId,
  189. UserId: 10000,
  190. Overwrite: true,
  191. }
  192. err := callSaveWithError(cmd)
  193. Convey("It should create dashboard guardian for dashboard with correct arguments and result in access denied error", func() {
  194. So(err, ShouldNotBeNil)
  195. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  196. So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedDashInGeneralFolder.Id)
  197. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  198. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  199. })
  200. })
  201. Convey("When updating a dashboard by existing id in other folder", func() {
  202. cmd := models.SaveDashboardCommand{
  203. OrgId: testOrgId,
  204. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  205. "id": savedDashInFolder.Id,
  206. "title": "Dash",
  207. }),
  208. FolderId: savedDashInFolder.FolderId,
  209. UserId: 10000,
  210. Overwrite: true,
  211. }
  212. err := callSaveWithError(cmd)
  213. Convey("It should create dashboard guardian for dashboard with correct arguments and result in access denied error", func() {
  214. So(err, ShouldNotBeNil)
  215. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  216. So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedDashInFolder.Id)
  217. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  218. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  219. })
  220. })
  221. Convey("When moving a dashboard by existing id to other folder from General folder", func() {
  222. cmd := models.SaveDashboardCommand{
  223. OrgId: testOrgId,
  224. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  225. "id": savedDashInGeneralFolder.Id,
  226. "title": "Dash",
  227. }),
  228. FolderId: otherSavedFolder.Id,
  229. UserId: 10000,
  230. Overwrite: true,
  231. }
  232. err := callSaveWithError(cmd)
  233. Convey("It should create dashboard guardian for other folder with correct arguments and result in access denied error", func() {
  234. So(err, ShouldNotBeNil)
  235. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  236. So(sc.dashboardGuardianMock.DashId, ShouldEqual, otherSavedFolder.Id)
  237. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  238. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  239. })
  240. })
  241. Convey("When moving a dashboard by existing id to the General folder from other folder", func() {
  242. cmd := models.SaveDashboardCommand{
  243. OrgId: testOrgId,
  244. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  245. "id": savedDashInFolder.Id,
  246. "title": "Dash",
  247. }),
  248. FolderId: 0,
  249. UserId: 10000,
  250. Overwrite: true,
  251. }
  252. err := callSaveWithError(cmd)
  253. Convey("It should create dashboard guardian for General folder with correct arguments and result in access denied error", func() {
  254. So(err, ShouldNotBeNil)
  255. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  256. So(sc.dashboardGuardianMock.DashId, ShouldEqual, 0)
  257. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  258. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  259. })
  260. })
  261. Convey("When moving a dashboard by existing uid to other folder from General folder", func() {
  262. cmd := models.SaveDashboardCommand{
  263. OrgId: testOrgId,
  264. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  265. "uid": savedDashInGeneralFolder.Uid,
  266. "title": "Dash",
  267. }),
  268. FolderId: otherSavedFolder.Id,
  269. UserId: 10000,
  270. Overwrite: true,
  271. }
  272. err := callSaveWithError(cmd)
  273. Convey("It should create dashboard guardian for other folder with correct arguments and result in access denied error", func() {
  274. So(err, ShouldNotBeNil)
  275. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  276. So(sc.dashboardGuardianMock.DashId, ShouldEqual, otherSavedFolder.Id)
  277. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  278. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  279. })
  280. })
  281. Convey("When moving a dashboard by existing uid to the General folder from other folder", func() {
  282. cmd := models.SaveDashboardCommand{
  283. OrgId: testOrgId,
  284. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  285. "uid": savedDashInFolder.Uid,
  286. "title": "Dash",
  287. }),
  288. FolderId: 0,
  289. UserId: 10000,
  290. Overwrite: true,
  291. }
  292. err := callSaveWithError(cmd)
  293. Convey("It should create dashboard guardian for General folder with correct arguments and result in access denied error", func() {
  294. So(err, ShouldNotBeNil)
  295. So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
  296. So(sc.dashboardGuardianMock.DashId, ShouldEqual, 0)
  297. So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
  298. So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
  299. })
  300. })
  301. })
  302. // Given user has permission to save
  303. permissionScenario("Given user has permission to save", true, func(sc *dashboardPermissionScenarioContext) {
  304. Convey("and overwrite flag is set to false", func() {
  305. shouldOverwrite := false
  306. Convey("When creating a dashboard in General folder with same name as dashboard in other folder", func() {
  307. cmd := models.SaveDashboardCommand{
  308. OrgId: testOrgId,
  309. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  310. "id": nil,
  311. "title": savedDashInFolder.Title,
  312. }),
  313. FolderId: 0,
  314. Overwrite: shouldOverwrite,
  315. }
  316. res := callSaveWithResult(cmd)
  317. So(res, ShouldNotBeNil)
  318. Convey("It should create a new dashboard", func() {
  319. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  320. err := bus.Dispatch(&query)
  321. So(err, ShouldBeNil)
  322. So(query.Result.Id, ShouldEqual, res.Id)
  323. So(query.Result.FolderId, ShouldEqual, 0)
  324. })
  325. })
  326. Convey("When creating a dashboard in other folder with same name as dashboard in General folder", func() {
  327. cmd := models.SaveDashboardCommand{
  328. OrgId: testOrgId,
  329. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  330. "id": nil,
  331. "title": savedDashInGeneralFolder.Title,
  332. }),
  333. FolderId: savedFolder.Id,
  334. Overwrite: shouldOverwrite,
  335. }
  336. res := callSaveWithResult(cmd)
  337. So(res, ShouldNotBeNil)
  338. Convey("It should create a new dashboard", func() {
  339. So(res.Id, ShouldNotEqual, savedDashInGeneralFolder.Id)
  340. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  341. err := bus.Dispatch(&query)
  342. So(err, ShouldBeNil)
  343. So(query.Result.FolderId, ShouldEqual, savedFolder.Id)
  344. })
  345. })
  346. Convey("When creating a folder with same name as dashboard in other folder", func() {
  347. cmd := models.SaveDashboardCommand{
  348. OrgId: testOrgId,
  349. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  350. "id": nil,
  351. "title": savedDashInFolder.Title,
  352. }),
  353. IsFolder: true,
  354. Overwrite: shouldOverwrite,
  355. }
  356. res := callSaveWithResult(cmd)
  357. So(res, ShouldNotBeNil)
  358. Convey("It should create a new folder", func() {
  359. So(res.Id, ShouldNotEqual, savedDashInGeneralFolder.Id)
  360. So(res.IsFolder, ShouldBeTrue)
  361. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  362. err := bus.Dispatch(&query)
  363. So(err, ShouldBeNil)
  364. So(query.Result.FolderId, ShouldEqual, 0)
  365. So(query.Result.IsFolder, ShouldBeTrue)
  366. })
  367. })
  368. Convey("When saving a dashboard without id and uid and unique title in folder", func() {
  369. cmd := models.SaveDashboardCommand{
  370. OrgId: testOrgId,
  371. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  372. "title": "Dash without id and uid",
  373. }),
  374. Overwrite: shouldOverwrite,
  375. }
  376. res := callSaveWithResult(cmd)
  377. So(res, ShouldNotBeNil)
  378. Convey("It should create a new dashboard", func() {
  379. So(res.Id, ShouldBeGreaterThan, 0)
  380. So(len(res.Uid), ShouldBeGreaterThan, 0)
  381. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  382. err := bus.Dispatch(&query)
  383. So(err, ShouldBeNil)
  384. So(query.Result.Id, ShouldEqual, res.Id)
  385. So(query.Result.Uid, ShouldEqual, res.Uid)
  386. })
  387. })
  388. Convey("When saving a dashboard when dashboard id is zero ", func() {
  389. cmd := models.SaveDashboardCommand{
  390. OrgId: testOrgId,
  391. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  392. "id": 0,
  393. "title": "Dash with zero id",
  394. }),
  395. Overwrite: shouldOverwrite,
  396. }
  397. res := callSaveWithResult(cmd)
  398. So(res, ShouldNotBeNil)
  399. Convey("It should create a new dashboard", func() {
  400. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  401. err := bus.Dispatch(&query)
  402. So(err, ShouldBeNil)
  403. So(query.Result.Id, ShouldEqual, res.Id)
  404. })
  405. })
  406. Convey("When saving a dashboard in non-existing folder", func() {
  407. cmd := models.SaveDashboardCommand{
  408. OrgId: testOrgId,
  409. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  410. "title": "Expect error",
  411. }),
  412. FolderId: 123412321,
  413. Overwrite: shouldOverwrite,
  414. }
  415. err := callSaveWithError(cmd)
  416. Convey("It should result in folder not found error", func() {
  417. So(err, ShouldNotBeNil)
  418. So(err, ShouldEqual, models.ErrDashboardFolderNotFound)
  419. })
  420. })
  421. Convey("When updating an existing dashboard by id without current version", func() {
  422. cmd := models.SaveDashboardCommand{
  423. OrgId: 1,
  424. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  425. "id": savedDashInGeneralFolder.Id,
  426. "title": "test dash 23",
  427. }),
  428. FolderId: savedFolder.Id,
  429. Overwrite: shouldOverwrite,
  430. }
  431. err := callSaveWithError(cmd)
  432. Convey("It should result in version mismatch error", func() {
  433. So(err, ShouldNotBeNil)
  434. So(err, ShouldEqual, models.ErrDashboardVersionMismatch)
  435. })
  436. })
  437. Convey("When updating an existing dashboard by id with current version", func() {
  438. cmd := models.SaveDashboardCommand{
  439. OrgId: 1,
  440. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  441. "id": savedDashInGeneralFolder.Id,
  442. "title": "Updated title",
  443. "version": savedDashInGeneralFolder.Version,
  444. }),
  445. FolderId: savedFolder.Id,
  446. Overwrite: shouldOverwrite,
  447. }
  448. res := callSaveWithResult(cmd)
  449. So(res, ShouldNotBeNil)
  450. Convey("It should update dashboard", func() {
  451. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: savedDashInGeneralFolder.Id}
  452. err := bus.Dispatch(&query)
  453. So(err, ShouldBeNil)
  454. So(query.Result.Title, ShouldEqual, "Updated title")
  455. So(query.Result.FolderId, ShouldEqual, savedFolder.Id)
  456. So(query.Result.Version, ShouldBeGreaterThan, savedDashInGeneralFolder.Version)
  457. })
  458. })
  459. Convey("When updating an existing dashboard by uid without current version", func() {
  460. cmd := models.SaveDashboardCommand{
  461. OrgId: 1,
  462. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  463. "uid": savedDashInFolder.Uid,
  464. "title": "test dash 23",
  465. }),
  466. FolderId: 0,
  467. Overwrite: shouldOverwrite,
  468. }
  469. err := callSaveWithError(cmd)
  470. Convey("It should result in version mismatch error", func() {
  471. So(err, ShouldNotBeNil)
  472. So(err, ShouldEqual, models.ErrDashboardVersionMismatch)
  473. })
  474. })
  475. Convey("When updating an existing dashboard by uid with current version", func() {
  476. cmd := models.SaveDashboardCommand{
  477. OrgId: 1,
  478. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  479. "uid": savedDashInFolder.Uid,
  480. "title": "Updated title",
  481. "version": savedDashInFolder.Version,
  482. }),
  483. FolderId: 0,
  484. Overwrite: shouldOverwrite,
  485. }
  486. res := callSaveWithResult(cmd)
  487. So(res, ShouldNotBeNil)
  488. Convey("It should update dashboard", func() {
  489. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: savedDashInFolder.Id}
  490. err := bus.Dispatch(&query)
  491. So(err, ShouldBeNil)
  492. So(query.Result.Title, ShouldEqual, "Updated title")
  493. So(query.Result.FolderId, ShouldEqual, 0)
  494. So(query.Result.Version, ShouldBeGreaterThan, savedDashInFolder.Version)
  495. })
  496. })
  497. Convey("When creating a dashboard with same name as dashboard in other folder", func() {
  498. cmd := models.SaveDashboardCommand{
  499. OrgId: testOrgId,
  500. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  501. "id": nil,
  502. "title": savedDashInFolder.Title,
  503. }),
  504. FolderId: savedDashInFolder.FolderId,
  505. Overwrite: shouldOverwrite,
  506. }
  507. err := callSaveWithError(cmd)
  508. Convey("It should result in dashboard with same name in folder error", func() {
  509. So(err, ShouldNotBeNil)
  510. So(err, ShouldEqual, models.ErrDashboardWithSameNameInFolderExists)
  511. })
  512. })
  513. Convey("When creating a dashboard with same name as dashboard in General folder", func() {
  514. cmd := models.SaveDashboardCommand{
  515. OrgId: testOrgId,
  516. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  517. "id": nil,
  518. "title": savedDashInGeneralFolder.Title,
  519. }),
  520. FolderId: savedDashInGeneralFolder.FolderId,
  521. Overwrite: shouldOverwrite,
  522. }
  523. err := callSaveWithError(cmd)
  524. Convey("It should result in dashboard with same name in folder error", func() {
  525. So(err, ShouldNotBeNil)
  526. So(err, ShouldEqual, models.ErrDashboardWithSameNameInFolderExists)
  527. })
  528. })
  529. Convey("When creating a folder with same name as existing folder", func() {
  530. cmd := models.SaveDashboardCommand{
  531. OrgId: testOrgId,
  532. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  533. "id": nil,
  534. "title": savedFolder.Title,
  535. }),
  536. IsFolder: true,
  537. Overwrite: shouldOverwrite,
  538. }
  539. err := callSaveWithError(cmd)
  540. Convey("It should result in dashboard with same name in folder error", func() {
  541. So(err, ShouldNotBeNil)
  542. So(err, ShouldEqual, models.ErrDashboardWithSameNameInFolderExists)
  543. })
  544. })
  545. })
  546. Convey("and overwrite flag is set to true", func() {
  547. shouldOverwrite := true
  548. Convey("When updating an existing dashboard by id without current version", func() {
  549. cmd := models.SaveDashboardCommand{
  550. OrgId: 1,
  551. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  552. "id": savedDashInGeneralFolder.Id,
  553. "title": "Updated title",
  554. }),
  555. FolderId: savedFolder.Id,
  556. Overwrite: shouldOverwrite,
  557. }
  558. res := callSaveWithResult(cmd)
  559. So(res, ShouldNotBeNil)
  560. Convey("It should update dashboard", func() {
  561. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: savedDashInGeneralFolder.Id}
  562. err := bus.Dispatch(&query)
  563. So(err, ShouldBeNil)
  564. So(query.Result.Title, ShouldEqual, "Updated title")
  565. So(query.Result.FolderId, ShouldEqual, savedFolder.Id)
  566. So(query.Result.Version, ShouldBeGreaterThan, savedDashInGeneralFolder.Version)
  567. })
  568. })
  569. Convey("When updating an existing dashboard by uid without current version", func() {
  570. cmd := models.SaveDashboardCommand{
  571. OrgId: 1,
  572. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  573. "uid": savedDashInFolder.Uid,
  574. "title": "Updated title",
  575. }),
  576. FolderId: 0,
  577. Overwrite: shouldOverwrite,
  578. }
  579. res := callSaveWithResult(cmd)
  580. So(res, ShouldNotBeNil)
  581. Convey("It should update dashboard", func() {
  582. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: savedDashInFolder.Id}
  583. err := bus.Dispatch(&query)
  584. So(err, ShouldBeNil)
  585. So(query.Result.Title, ShouldEqual, "Updated title")
  586. So(query.Result.FolderId, ShouldEqual, 0)
  587. So(query.Result.Version, ShouldBeGreaterThan, savedDashInFolder.Version)
  588. })
  589. })
  590. Convey("When updating uid for existing dashboard using id", func() {
  591. cmd := models.SaveDashboardCommand{
  592. OrgId: 1,
  593. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  594. "id": savedDashInFolder.Id,
  595. "uid": "new-uid",
  596. "title": savedDashInFolder.Title,
  597. }),
  598. Overwrite: shouldOverwrite,
  599. }
  600. res := callSaveWithResult(cmd)
  601. Convey("It should update dashboard", func() {
  602. So(res, ShouldNotBeNil)
  603. So(res.Id, ShouldEqual, savedDashInFolder.Id)
  604. So(res.Uid, ShouldEqual, "new-uid")
  605. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: savedDashInFolder.Id}
  606. err := bus.Dispatch(&query)
  607. So(err, ShouldBeNil)
  608. So(query.Result.Uid, ShouldEqual, "new-uid")
  609. So(query.Result.Version, ShouldBeGreaterThan, savedDashInFolder.Version)
  610. })
  611. })
  612. Convey("When updating uid to an existing uid for existing dashboard using id", func() {
  613. cmd := models.SaveDashboardCommand{
  614. OrgId: 1,
  615. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  616. "id": savedDashInFolder.Id,
  617. "uid": savedDashInGeneralFolder.Uid,
  618. "title": savedDashInFolder.Title,
  619. }),
  620. Overwrite: shouldOverwrite,
  621. }
  622. err := callSaveWithError(cmd)
  623. Convey("It should result in same uid exists error", func() {
  624. So(err, ShouldNotBeNil)
  625. So(err, ShouldEqual, models.ErrDashboardWithSameUIDExists)
  626. })
  627. })
  628. Convey("When creating a dashboard with same name as dashboard in other folder", func() {
  629. cmd := models.SaveDashboardCommand{
  630. OrgId: testOrgId,
  631. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  632. "id": nil,
  633. "title": savedDashInFolder.Title,
  634. }),
  635. FolderId: savedDashInFolder.FolderId,
  636. Overwrite: shouldOverwrite,
  637. }
  638. res := callSaveWithResult(cmd)
  639. Convey("It should overwrite existing dashboard", func() {
  640. So(res, ShouldNotBeNil)
  641. So(res.Id, ShouldEqual, savedDashInFolder.Id)
  642. So(res.Uid, ShouldEqual, savedDashInFolder.Uid)
  643. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  644. err := bus.Dispatch(&query)
  645. So(err, ShouldBeNil)
  646. So(query.Result.Id, ShouldEqual, res.Id)
  647. So(query.Result.Uid, ShouldEqual, res.Uid)
  648. })
  649. })
  650. Convey("When creating a dashboard with same name as dashboard in General folder", func() {
  651. cmd := models.SaveDashboardCommand{
  652. OrgId: testOrgId,
  653. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  654. "id": nil,
  655. "title": savedDashInGeneralFolder.Title,
  656. }),
  657. FolderId: savedDashInGeneralFolder.FolderId,
  658. Overwrite: shouldOverwrite,
  659. }
  660. res := callSaveWithResult(cmd)
  661. Convey("It should overwrite existing dashboard", func() {
  662. So(res, ShouldNotBeNil)
  663. So(res.Id, ShouldEqual, savedDashInGeneralFolder.Id)
  664. So(res.Uid, ShouldEqual, savedDashInGeneralFolder.Uid)
  665. query := models.GetDashboardQuery{OrgId: cmd.OrgId, Id: res.Id}
  666. err := bus.Dispatch(&query)
  667. So(err, ShouldBeNil)
  668. So(query.Result.Id, ShouldEqual, res.Id)
  669. So(query.Result.Uid, ShouldEqual, res.Uid)
  670. })
  671. })
  672. Convey("When updating existing folder to a dashboard using id", func() {
  673. cmd := models.SaveDashboardCommand{
  674. OrgId: 1,
  675. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  676. "id": savedFolder.Id,
  677. "title": "new title",
  678. }),
  679. IsFolder: false,
  680. Overwrite: shouldOverwrite,
  681. }
  682. err := callSaveWithError(cmd)
  683. Convey("It should result in type mismatch error", func() {
  684. So(err, ShouldNotBeNil)
  685. So(err, ShouldEqual, models.ErrDashboardTypeMismatch)
  686. })
  687. })
  688. Convey("When updating existing dashboard to a folder using id", func() {
  689. cmd := models.SaveDashboardCommand{
  690. OrgId: 1,
  691. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  692. "id": savedDashInFolder.Id,
  693. "title": "new folder title",
  694. }),
  695. IsFolder: true,
  696. Overwrite: shouldOverwrite,
  697. }
  698. err := callSaveWithError(cmd)
  699. Convey("It should result in type mismatch error", func() {
  700. So(err, ShouldNotBeNil)
  701. So(err, ShouldEqual, models.ErrDashboardTypeMismatch)
  702. })
  703. })
  704. Convey("When updating existing folder to a dashboard using uid", func() {
  705. cmd := models.SaveDashboardCommand{
  706. OrgId: 1,
  707. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  708. "uid": savedFolder.Uid,
  709. "title": "new title",
  710. }),
  711. IsFolder: false,
  712. Overwrite: shouldOverwrite,
  713. }
  714. err := callSaveWithError(cmd)
  715. Convey("It should result in type mismatch error", func() {
  716. So(err, ShouldNotBeNil)
  717. So(err, ShouldEqual, models.ErrDashboardTypeMismatch)
  718. })
  719. })
  720. Convey("When updating existing dashboard to a folder using uid", func() {
  721. cmd := models.SaveDashboardCommand{
  722. OrgId: 1,
  723. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  724. "uid": savedDashInFolder.Uid,
  725. "title": "new folder title",
  726. }),
  727. IsFolder: true,
  728. Overwrite: shouldOverwrite,
  729. }
  730. err := callSaveWithError(cmd)
  731. Convey("It should result in type mismatch error", func() {
  732. So(err, ShouldNotBeNil)
  733. So(err, ShouldEqual, models.ErrDashboardTypeMismatch)
  734. })
  735. })
  736. Convey("When updating existing folder to a dashboard using title", func() {
  737. cmd := models.SaveDashboardCommand{
  738. OrgId: 1,
  739. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  740. "title": savedFolder.Title,
  741. }),
  742. IsFolder: false,
  743. Overwrite: shouldOverwrite,
  744. }
  745. err := callSaveWithError(cmd)
  746. Convey("It should result in dashboard with same name as folder error", func() {
  747. So(err, ShouldNotBeNil)
  748. So(err, ShouldEqual, models.ErrDashboardWithSameNameAsFolder)
  749. })
  750. })
  751. Convey("When updating existing dashboard to a folder using title", func() {
  752. cmd := models.SaveDashboardCommand{
  753. OrgId: 1,
  754. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  755. "title": savedDashInGeneralFolder.Title,
  756. }),
  757. IsFolder: true,
  758. Overwrite: shouldOverwrite,
  759. }
  760. err := callSaveWithError(cmd)
  761. Convey("It should result in folder with same name as dashboard error", func() {
  762. So(err, ShouldNotBeNil)
  763. So(err, ShouldEqual, models.ErrDashboardFolderWithSameNameAsDashboard)
  764. })
  765. })
  766. })
  767. })
  768. })
  769. })
  770. }
  771. type dashboardPermissionScenarioContext struct {
  772. dashboardGuardianMock *guardian.FakeDashboardGuardian
  773. }
  774. type dashboardPermissionScenarioFunc func(sc *dashboardPermissionScenarioContext)
  775. func dashboardPermissionScenario(desc string, mock *guardian.FakeDashboardGuardian, fn dashboardPermissionScenarioFunc) {
  776. Convey(desc, func() {
  777. origNewDashboardGuardian := guardian.New
  778. guardian.MockDashboardGuardian(mock)
  779. sc := &dashboardPermissionScenarioContext{
  780. dashboardGuardianMock: mock,
  781. }
  782. defer func() {
  783. guardian.New = origNewDashboardGuardian
  784. }()
  785. fn(sc)
  786. })
  787. }
  788. func permissionScenario(desc string, canSave bool, fn dashboardPermissionScenarioFunc) {
  789. mock := &guardian.FakeDashboardGuardian{
  790. CanSaveValue: canSave,
  791. }
  792. dashboardPermissionScenario(desc, mock, fn)
  793. }
  794. func callSaveWithResult(cmd models.SaveDashboardCommand) *models.Dashboard {
  795. dto := toSaveDashboardDto(cmd)
  796. res, _ := dashboards.NewService().SaveDashboard(&dto)
  797. return res
  798. }
  799. func callSaveWithError(cmd models.SaveDashboardCommand) error {
  800. dto := toSaveDashboardDto(cmd)
  801. _, err := dashboards.NewService().SaveDashboard(&dto)
  802. return err
  803. }
  804. func saveTestDashboard(title string, orgId int64, folderId int64) *models.Dashboard {
  805. cmd := models.SaveDashboardCommand{
  806. OrgId: orgId,
  807. FolderId: folderId,
  808. IsFolder: false,
  809. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  810. "id": nil,
  811. "title": title,
  812. }),
  813. }
  814. dto := dashboards.SaveDashboardDTO{
  815. OrgId: orgId,
  816. Dashboard: cmd.GetDashboardModel(),
  817. User: &models.SignedInUser{
  818. UserId: 1,
  819. OrgRole: models.ROLE_ADMIN,
  820. },
  821. }
  822. res, err := dashboards.NewService().SaveDashboard(&dto)
  823. So(err, ShouldBeNil)
  824. return res
  825. }
  826. func saveTestFolder(title string, orgId int64) *models.Dashboard {
  827. cmd := models.SaveDashboardCommand{
  828. OrgId: orgId,
  829. FolderId: 0,
  830. IsFolder: true,
  831. Dashboard: simplejson.NewFromAny(map[string]interface{}{
  832. "id": nil,
  833. "title": title,
  834. }),
  835. }
  836. dto := dashboards.SaveDashboardDTO{
  837. OrgId: orgId,
  838. Dashboard: cmd.GetDashboardModel(),
  839. User: &models.SignedInUser{
  840. UserId: 1,
  841. OrgRole: models.ROLE_ADMIN,
  842. },
  843. }
  844. res, err := dashboards.NewService().SaveDashboard(&dto)
  845. So(err, ShouldBeNil)
  846. return res
  847. }
  848. func toSaveDashboardDto(cmd models.SaveDashboardCommand) dashboards.SaveDashboardDTO {
  849. dash := (&cmd).GetDashboardModel()
  850. return dashboards.SaveDashboardDTO{
  851. Dashboard: dash,
  852. Message: cmd.Message,
  853. OrgId: cmd.OrgId,
  854. User: &models.SignedInUser{UserId: cmd.UserId},
  855. Overwrite: cmd.Overwrite,
  856. }
  857. }