annotations_test.go 9.2 KB

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