annotations_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package api
  2. import (
  3. "testing"
  4. "github.com/grafana/grafana/pkg/api/dtos"
  5. "github.com/grafana/grafana/pkg/bus"
  6. m "github.com/grafana/grafana/pkg/models"
  7. "github.com/grafana/grafana/pkg/services/annotations"
  8. . "github.com/smartystreets/goconvey/convey"
  9. )
  10. func TestAnnotationsApiEndpoint(t *testing.T) {
  11. Convey("Given an annotation without a dashboard id", t, func() {
  12. cmd := dtos.PostAnnotationsCmd{
  13. Time: 1000,
  14. Text: "annotation text",
  15. Tags: []string{"tag1", "tag2"},
  16. IsRegion: false,
  17. }
  18. updateCmd := dtos.UpdateAnnotationsCmd{
  19. Time: 1000,
  20. Text: "annotation text",
  21. Tags: []string{"tag1", "tag2"},
  22. IsRegion: false,
  23. }
  24. patchCmd := dtos.PatchAnnotationsCmd{
  25. Time: 1000,
  26. Text: "annotation text",
  27. Tags: []string{"tag1", "tag2"},
  28. }
  29. Convey("When user is an Org Viewer", func() {
  30. role := m.ROLE_VIEWER
  31. Convey("Should not be allowed to save an annotation", func() {
  32. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  33. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  34. So(sc.resp.Code, ShouldEqual, 403)
  35. })
  36. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  37. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  38. So(sc.resp.Code, ShouldEqual, 403)
  39. })
  40. patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
  41. sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
  42. So(sc.resp.Code, ShouldEqual, 403)
  43. })
  44. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  45. sc.handlerFunc = DeleteAnnotationByID
  46. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  47. So(sc.resp.Code, ShouldEqual, 403)
  48. })
  49. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  50. sc.handlerFunc = DeleteAnnotationRegion
  51. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  52. So(sc.resp.Code, ShouldEqual, 403)
  53. })
  54. })
  55. })
  56. Convey("When user is an Org Editor", func() {
  57. role := m.ROLE_EDITOR
  58. Convey("Should be able to save an annotation", func() {
  59. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  60. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  61. So(sc.resp.Code, ShouldEqual, 200)
  62. })
  63. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  64. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  65. So(sc.resp.Code, ShouldEqual, 200)
  66. })
  67. patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
  68. sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
  69. So(sc.resp.Code, ShouldEqual, 200)
  70. })
  71. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  72. sc.handlerFunc = DeleteAnnotationByID
  73. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  74. So(sc.resp.Code, ShouldEqual, 200)
  75. })
  76. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  77. sc.handlerFunc = DeleteAnnotationRegion
  78. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  79. So(sc.resp.Code, ShouldEqual, 200)
  80. })
  81. })
  82. })
  83. })
  84. Convey("Given an annotation with a dashboard id and the dashboard does not have an acl", t, func() {
  85. cmd := dtos.PostAnnotationsCmd{
  86. Time: 1000,
  87. Text: "annotation text",
  88. Tags: []string{"tag1", "tag2"},
  89. IsRegion: false,
  90. DashboardId: 1,
  91. PanelId: 1,
  92. }
  93. updateCmd := dtos.UpdateAnnotationsCmd{
  94. Time: 1000,
  95. Text: "annotation text",
  96. Tags: []string{"tag1", "tag2"},
  97. IsRegion: false,
  98. Id: 1,
  99. }
  100. patchCmd := dtos.PatchAnnotationsCmd{
  101. Time: 8000,
  102. Text: "annotation text 50",
  103. Tags: []string{"foo", "bar"},
  104. Id: 1,
  105. }
  106. deleteCmd := dtos.DeleteAnnotationsCmd{
  107. DashboardId: 1,
  108. PanelId: 1,
  109. }
  110. viewerRole := m.ROLE_VIEWER
  111. editorRole := m.ROLE_EDITOR
  112. aclMockResp := []*m.DashboardAclInfoDTO{
  113. {Role: &viewerRole, Permission: m.PERMISSION_VIEW},
  114. {Role: &editorRole, Permission: m.PERMISSION_EDIT},
  115. }
  116. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  117. query.Result = aclMockResp
  118. return nil
  119. })
  120. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  121. query.Result = []*m.TeamDTO{}
  122. return nil
  123. })
  124. Convey("When user is an Org Viewer", func() {
  125. role := m.ROLE_VIEWER
  126. Convey("Should not be allowed to save an annotation", func() {
  127. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  128. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  129. So(sc.resp.Code, ShouldEqual, 403)
  130. })
  131. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  132. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  133. So(sc.resp.Code, ShouldEqual, 403)
  134. })
  135. patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
  136. sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
  137. So(sc.resp.Code, ShouldEqual, 403)
  138. })
  139. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  140. sc.handlerFunc = DeleteAnnotationByID
  141. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  142. So(sc.resp.Code, ShouldEqual, 403)
  143. })
  144. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  145. sc.handlerFunc = DeleteAnnotationRegion
  146. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  147. So(sc.resp.Code, ShouldEqual, 403)
  148. })
  149. })
  150. })
  151. Convey("When user is an Org Editor", func() {
  152. role := m.ROLE_EDITOR
  153. Convey("Should be able to save an annotation", func() {
  154. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  155. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  156. So(sc.resp.Code, ShouldEqual, 200)
  157. })
  158. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  159. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  160. So(sc.resp.Code, ShouldEqual, 200)
  161. })
  162. patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
  163. sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
  164. So(sc.resp.Code, ShouldEqual, 200)
  165. })
  166. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  167. sc.handlerFunc = DeleteAnnotationByID
  168. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  169. So(sc.resp.Code, ShouldEqual, 200)
  170. })
  171. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  172. sc.handlerFunc = DeleteAnnotationRegion
  173. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  174. So(sc.resp.Code, ShouldEqual, 200)
  175. })
  176. })
  177. })
  178. Convey("When user is an Admin", func() {
  179. role := m.ROLE_ADMIN
  180. Convey("Should be able to do anything", func() {
  181. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  182. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  183. So(sc.resp.Code, ShouldEqual, 200)
  184. })
  185. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  186. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  187. So(sc.resp.Code, ShouldEqual, 200)
  188. })
  189. patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
  190. sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
  191. So(sc.resp.Code, ShouldEqual, 200)
  192. })
  193. deleteAnnotationsScenario("When calling POST on", "/api/annotations/mass-delete", "/api/annotations/mass-delete", role, deleteCmd, func(sc *scenarioContext) {
  194. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  195. So(sc.resp.Code, ShouldEqual, 200)
  196. })
  197. })
  198. })
  199. })
  200. }
  201. type fakeAnnotationsRepo struct {
  202. }
  203. func (repo *fakeAnnotationsRepo) Delete(params *annotations.DeleteParams) error {
  204. return nil
  205. }
  206. func (repo *fakeAnnotationsRepo) Save(item *annotations.Item) error {
  207. item.Id = 1
  208. return nil
  209. }
  210. func (repo *fakeAnnotationsRepo) Update(item *annotations.Item) error {
  211. return nil
  212. }
  213. func (repo *fakeAnnotationsRepo) Find(query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
  214. annotations := []*annotations.ItemDTO{{Id: 1}}
  215. return annotations, nil
  216. }
  217. var fakeAnnoRepo *fakeAnnotationsRepo
  218. func postAnnotationScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.PostAnnotationsCmd, fn scenarioFunc) {
  219. Convey(desc+" "+url, func() {
  220. defer bus.ClearBusHandlers()
  221. sc := setupScenarioContext(url)
  222. sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
  223. sc.context = c
  224. sc.context.UserId = TestUserID
  225. sc.context.OrgId = TestOrgID
  226. sc.context.OrgRole = role
  227. return PostAnnotation(c, cmd)
  228. })
  229. fakeAnnoRepo = &fakeAnnotationsRepo{}
  230. annotations.SetRepository(fakeAnnoRepo)
  231. sc.m.Post(routePattern, sc.defaultHandler)
  232. fn(sc)
  233. })
  234. }
  235. func putAnnotationScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.UpdateAnnotationsCmd, fn scenarioFunc) {
  236. Convey(desc+" "+url, func() {
  237. defer bus.ClearBusHandlers()
  238. sc := setupScenarioContext(url)
  239. sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
  240. sc.context = c
  241. sc.context.UserId = TestUserID
  242. sc.context.OrgId = TestOrgID
  243. sc.context.OrgRole = role
  244. return UpdateAnnotation(c, cmd)
  245. })
  246. fakeAnnoRepo = &fakeAnnotationsRepo{}
  247. annotations.SetRepository(fakeAnnoRepo)
  248. sc.m.Put(routePattern, sc.defaultHandler)
  249. fn(sc)
  250. })
  251. }
  252. func patchAnnotationScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.PatchAnnotationsCmd, fn scenarioFunc) {
  253. Convey(desc+" "+url, func() {
  254. defer bus.ClearBusHandlers()
  255. sc := setupScenarioContext(url)
  256. sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
  257. sc.context = c
  258. sc.context.UserId = TestUserID
  259. sc.context.OrgId = TestOrgID
  260. sc.context.OrgRole = role
  261. return PatchAnnotation(c, cmd)
  262. })
  263. fakeAnnoRepo = &fakeAnnotationsRepo{}
  264. annotations.SetRepository(fakeAnnoRepo)
  265. sc.m.Patch(routePattern, sc.defaultHandler)
  266. fn(sc)
  267. })
  268. }
  269. func deleteAnnotationsScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.DeleteAnnotationsCmd, fn scenarioFunc) {
  270. Convey(desc+" "+url, func() {
  271. defer bus.ClearBusHandlers()
  272. sc := setupScenarioContext(url)
  273. sc.defaultHandler = Wrap(func(c *m.ReqContext) Response {
  274. sc.context = c
  275. sc.context.UserId = TestUserID
  276. sc.context.OrgId = TestOrgID
  277. sc.context.OrgRole = role
  278. return DeleteAnnotations(c, cmd)
  279. })
  280. fakeAnnoRepo = &fakeAnnotationsRepo{}
  281. annotations.SetRepository(fakeAnnoRepo)
  282. sc.m.Post(routePattern, sc.defaultHandler)
  283. fn(sc)
  284. })
  285. }