annotations_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. DashboardId: 1,
  14. Time: 1000,
  15. Text: "annotation text",
  16. Tags: []string{"tag1", "tag2"},
  17. IsRegion: false,
  18. }
  19. updateCmd := dtos.UpdateAnnotationsCmd{
  20. Time: 1000,
  21. Text: "annotation text",
  22. Tags: []string{"tag1", "tag2"},
  23. IsRegion: false,
  24. }
  25. Convey("When user is an Org Viewer", func() {
  26. role := m.ROLE_VIEWER
  27. Convey("Should not be allowed to save an annotation", func() {
  28. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  29. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  30. So(sc.resp.Code, ShouldEqual, 403)
  31. })
  32. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  33. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  34. So(sc.resp.Code, ShouldEqual, 403)
  35. })
  36. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  37. sc.handlerFunc = DeleteAnnotationById
  38. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  39. So(sc.resp.Code, ShouldEqual, 403)
  40. })
  41. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  42. sc.handlerFunc = DeleteAnnotationRegion
  43. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  44. So(sc.resp.Code, ShouldEqual, 403)
  45. })
  46. })
  47. })
  48. Convey("When user is an Org Editor", func() {
  49. role := m.ROLE_EDITOR
  50. Convey("Should be able to save an annotation", func() {
  51. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  52. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  53. So(sc.resp.Code, ShouldEqual, 200)
  54. })
  55. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  56. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  57. So(sc.resp.Code, ShouldEqual, 200)
  58. })
  59. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  60. sc.handlerFunc = DeleteAnnotationById
  61. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  62. So(sc.resp.Code, ShouldEqual, 200)
  63. })
  64. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  65. sc.handlerFunc = DeleteAnnotationRegion
  66. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  67. So(sc.resp.Code, ShouldEqual, 200)
  68. })
  69. })
  70. Convey("Should note be able to save an annotation", func() {
  71. cmd = dtos.PostAnnotationsCmd{
  72. Time: 1000,
  73. Text: "annotation text",
  74. }
  75. postAnnotationScenario("When calling POST without dashboardId", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  76. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  77. So(sc.resp.Code, ShouldEqual, 500)
  78. })
  79. cmd = dtos.PostAnnotationsCmd{
  80. Time: 1000,
  81. DashboardId: 3,
  82. }
  83. postAnnotationScenario("When calling POST without text", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  84. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  85. So(sc.resp.Code, ShouldEqual, 500)
  86. })
  87. })
  88. })
  89. })
  90. Convey("Given an annotation with a dashboard id and the dashboard does not have an acl", t, func() {
  91. cmd := dtos.PostAnnotationsCmd{
  92. Time: 1000,
  93. Text: "annotation text",
  94. Tags: []string{"tag1", "tag2"},
  95. IsRegion: false,
  96. DashboardId: 1,
  97. PanelId: 1,
  98. }
  99. updateCmd := dtos.UpdateAnnotationsCmd{
  100. Time: 1000,
  101. Text: "annotation text",
  102. Tags: []string{"tag1", "tag2"},
  103. IsRegion: false,
  104. Id: 1,
  105. }
  106. viewerRole := m.ROLE_VIEWER
  107. editorRole := m.ROLE_EDITOR
  108. aclMockResp := []*m.DashboardAclInfoDTO{
  109. {Role: &viewerRole, Permission: m.PERMISSION_VIEW},
  110. {Role: &editorRole, Permission: m.PERMISSION_EDIT},
  111. }
  112. bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
  113. query.Result = aclMockResp
  114. return nil
  115. })
  116. bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
  117. query.Result = []*m.Team{}
  118. return nil
  119. })
  120. Convey("When user is an Org Viewer", func() {
  121. role := m.ROLE_VIEWER
  122. Convey("Should not be allowed to save an annotation", func() {
  123. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  124. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  125. So(sc.resp.Code, ShouldEqual, 403)
  126. })
  127. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  128. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  129. So(sc.resp.Code, ShouldEqual, 403)
  130. })
  131. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  132. sc.handlerFunc = DeleteAnnotationById
  133. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  134. So(sc.resp.Code, ShouldEqual, 403)
  135. })
  136. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  137. sc.handlerFunc = DeleteAnnotationRegion
  138. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  139. So(sc.resp.Code, ShouldEqual, 403)
  140. })
  141. })
  142. })
  143. Convey("When user is an Org Editor", func() {
  144. role := m.ROLE_EDITOR
  145. Convey("Should be able to save an annotation", func() {
  146. postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
  147. sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
  148. So(sc.resp.Code, ShouldEqual, 200)
  149. })
  150. putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
  151. sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
  152. So(sc.resp.Code, ShouldEqual, 200)
  153. })
  154. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
  155. sc.handlerFunc = DeleteAnnotationById
  156. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  157. So(sc.resp.Code, ShouldEqual, 200)
  158. })
  159. loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/region/1", "/api/annotations/region/:regionId", role, func(sc *scenarioContext) {
  160. sc.handlerFunc = DeleteAnnotationRegion
  161. sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
  162. So(sc.resp.Code, ShouldEqual, 200)
  163. })
  164. })
  165. })
  166. })
  167. }
  168. type fakeAnnotationsRepo struct {
  169. }
  170. func (repo *fakeAnnotationsRepo) Delete(params *annotations.DeleteParams) error {
  171. return nil
  172. }
  173. func (repo *fakeAnnotationsRepo) Save(item *annotations.Item) error {
  174. item.Id = 1
  175. return nil
  176. }
  177. func (repo *fakeAnnotationsRepo) Update(item *annotations.Item) error {
  178. return nil
  179. }
  180. func (repo *fakeAnnotationsRepo) Find(query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
  181. annotations := []*annotations.ItemDTO{{Id: 1}}
  182. return annotations, nil
  183. }
  184. var fakeAnnoRepo *fakeAnnotationsRepo
  185. func postAnnotationScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.PostAnnotationsCmd, fn scenarioFunc) {
  186. Convey(desc+" "+url, func() {
  187. defer bus.ClearBusHandlers()
  188. sc := setupScenarioContext(url)
  189. sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
  190. sc.context = c
  191. sc.context.UserId = TestUserID
  192. sc.context.OrgId = TestOrgID
  193. sc.context.OrgRole = role
  194. return PostAnnotation(c, cmd)
  195. })
  196. fakeAnnoRepo = &fakeAnnotationsRepo{}
  197. annotations.SetRepository(fakeAnnoRepo)
  198. sc.m.Post(routePattern, sc.defaultHandler)
  199. fn(sc)
  200. })
  201. }
  202. func putAnnotationScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.UpdateAnnotationsCmd, fn scenarioFunc) {
  203. Convey(desc+" "+url, func() {
  204. defer bus.ClearBusHandlers()
  205. sc := setupScenarioContext(url)
  206. sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
  207. sc.context = c
  208. sc.context.UserId = TestUserID
  209. sc.context.OrgId = TestOrgID
  210. sc.context.OrgRole = role
  211. return UpdateAnnotation(c, cmd)
  212. })
  213. fakeAnnoRepo = &fakeAnnotationsRepo{}
  214. annotations.SetRepository(fakeAnnoRepo)
  215. sc.m.Put(routePattern, sc.defaultHandler)
  216. fn(sc)
  217. })
  218. }