test_organization_event_details.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. from __future__ import absolute_import
  2. from datetime import timedelta
  3. from django.core.urlresolvers import reverse
  4. from sentry.testutils import APITestCase, SnubaTestCase
  5. from sentry.testutils.helpers.datetime import iso_format, before_now
  6. from sentry.models import Group
  7. class OrganizationEventDetailsEndpointTest(APITestCase, SnubaTestCase):
  8. def setUp(self):
  9. super(OrganizationEventDetailsEndpointTest, self).setUp()
  10. min_ago = iso_format(before_now(minutes=1))
  11. two_min_ago = iso_format(before_now(minutes=2))
  12. three_min_ago = iso_format(before_now(minutes=3))
  13. self.login_as(user=self.user)
  14. self.project = self.create_project()
  15. self.store_event(
  16. data={
  17. "event_id": "a" * 32,
  18. "message": "oh no",
  19. "timestamp": three_min_ago,
  20. "fingerprint": ["group-1"],
  21. },
  22. project_id=self.project.id,
  23. )
  24. self.store_event(
  25. data={
  26. "event_id": "b" * 32,
  27. "message": "very bad",
  28. "timestamp": two_min_ago,
  29. "fingerprint": ["group-1"],
  30. },
  31. project_id=self.project.id,
  32. )
  33. self.store_event(
  34. data={
  35. "event_id": "c" * 32,
  36. "message": "very bad",
  37. "timestamp": min_ago,
  38. "fingerprint": ["group-2"],
  39. },
  40. project_id=self.project.id,
  41. )
  42. self.groups = list(Group.objects.all().order_by("id"))
  43. def test_simple(self):
  44. url = reverse(
  45. "sentry-api-0-organization-event-details",
  46. kwargs={
  47. "organization_slug": self.project.organization.slug,
  48. "project_slug": self.project.slug,
  49. "event_id": "a" * 32,
  50. },
  51. )
  52. with self.feature("organizations:events-v2"):
  53. response = self.client.get(url, format="json")
  54. assert response.status_code == 200, response.content
  55. assert response.data["id"] == "a" * 32
  56. assert response.data["previousEventID"] is None
  57. assert response.data["nextEventID"] == "b" * 32
  58. assert response.data["projectSlug"] == self.project.slug
  59. def test_simple_transaction(self):
  60. min_ago = iso_format(before_now(minutes=1))
  61. event = self.store_event(
  62. data={
  63. "event_id": "d" * 32,
  64. "type": "transaction",
  65. "transaction": "api.issue.delete",
  66. "spans": [],
  67. "contexts": {"trace": {"op": "foobar", "trace_id": "a" * 32, "span_id": "a" * 16}},
  68. "start_timestamp": iso_format(before_now(minutes=1, seconds=5)),
  69. "timestamp": min_ago,
  70. },
  71. project_id=self.project.id,
  72. )
  73. url = reverse(
  74. "sentry-api-0-organization-event-details",
  75. kwargs={
  76. "organization_slug": self.project.organization.slug,
  77. "project_slug": self.project.slug,
  78. "event_id": event.event_id,
  79. },
  80. )
  81. with self.feature("organizations:events-v2"):
  82. response = self.client.get(url, format="json")
  83. assert response.status_code == 200
  84. def test_no_access_missing_feature(self):
  85. url = reverse(
  86. "sentry-api-0-organization-event-details",
  87. kwargs={
  88. "organization_slug": self.project.organization.slug,
  89. "project_slug": self.project.slug,
  90. "event_id": "a" * 32,
  91. },
  92. )
  93. response = self.client.get(url, format="json")
  94. assert response.status_code == 404, response.content
  95. def test_access_non_member_project(self):
  96. # Add a new user to a project and then access events on project they are not part of.
  97. member_user = self.create_user()
  98. team = self.create_team(members=[member_user])
  99. self.create_project(organization=self.organization, teams=[team])
  100. # Enable open membership
  101. self.organization.flags.allow_joinleave = True
  102. self.organization.save()
  103. self.login_as(member_user)
  104. url = reverse(
  105. "sentry-api-0-organization-event-details",
  106. kwargs={
  107. "organization_slug": self.organization.slug,
  108. "project_slug": self.project.slug,
  109. "event_id": "a" * 32,
  110. },
  111. )
  112. with self.feature("organizations:events-v2"):
  113. response = self.client.get(url, format="json")
  114. assert response.status_code == 200, response.content
  115. # When open membership is off, access should be denied to non owner users
  116. self.organization.flags.allow_joinleave = False
  117. self.organization.save()
  118. with self.feature("organizations:events-v2"):
  119. response = self.client.get(url, format="json")
  120. assert response.status_code == 404, response.content
  121. def test_no_event(self):
  122. url = reverse(
  123. "sentry-api-0-organization-event-details",
  124. kwargs={
  125. "organization_slug": self.project.organization.slug,
  126. "project_slug": self.project.slug,
  127. "event_id": "d" * 32,
  128. },
  129. )
  130. with self.feature("organizations:events-v2"):
  131. response = self.client.get(url, format="json")
  132. assert response.status_code == 404, response.content
  133. def test_event_links_with_field_parameter(self):
  134. # Create older and newer events
  135. ten_sec_ago = iso_format(before_now(seconds=10))
  136. self.store_event(
  137. data={"event_id": "2" * 32, "message": "no match", "timestamp": ten_sec_ago},
  138. project_id=self.project.id,
  139. )
  140. thirty_sec_ago = iso_format(before_now(seconds=30))
  141. self.store_event(
  142. data={"event_id": "1" * 32, "message": "very bad", "timestamp": thirty_sec_ago},
  143. project_id=self.project.id,
  144. )
  145. five_min_ago = iso_format(before_now(minutes=5))
  146. self.store_event(
  147. data={"event_id": "d" * 32, "message": "very bad", "timestamp": five_min_ago},
  148. project_id=self.project.id,
  149. )
  150. seven_min_ago = iso_format(before_now(minutes=7))
  151. self.store_event(
  152. data={"event_id": "e" * 32, "message": "very bad", "timestamp": seven_min_ago},
  153. project_id=self.project.id,
  154. )
  155. eight_min_ago = iso_format(before_now(minutes=8))
  156. self.store_event(
  157. data={"event_id": "f" * 32, "message": "no match", "timestamp": eight_min_ago},
  158. project_id=self.project.id,
  159. )
  160. url = reverse(
  161. "sentry-api-0-organization-event-details",
  162. kwargs={
  163. "organization_slug": self.project.organization.slug,
  164. "project_slug": self.project.slug,
  165. "event_id": "b" * 32,
  166. },
  167. )
  168. with self.feature("organizations:events-v2"):
  169. response = self.client.get(url, format="json", data={"field": ["message", "count()"]})
  170. assert response.data["eventID"] == "b" * 32
  171. assert response.data["nextEventID"] == "c" * 32, "c is newer & matches message"
  172. assert response.data["previousEventID"] == "d" * 32, "d is older & matches message"
  173. assert response.data["oldestEventID"] == "e" * 32, "e is oldest matching message"
  174. assert response.data["latestEventID"] == "1" * 32, "1 is newest matching message"
  175. def test_event_links_with_date_range(self):
  176. # Create older in and out of range events
  177. ten_day_ago = iso_format(before_now(days=14))
  178. self.store_event(
  179. data={"event_id": "3" * 32, "message": "very bad", "timestamp": ten_day_ago},
  180. project_id=self.project.id,
  181. )
  182. seven_min_ago = iso_format(before_now(minutes=7))
  183. self.store_event(
  184. data={"event_id": "2" * 32, "message": "very bad", "timestamp": seven_min_ago},
  185. project_id=self.project.id,
  186. )
  187. url = reverse(
  188. "sentry-api-0-organization-event-details",
  189. kwargs={
  190. "organization_slug": self.project.organization.slug,
  191. "project_slug": self.project.slug,
  192. "event_id": "b" * 32,
  193. },
  194. )
  195. with self.feature("organizations:events-v2"):
  196. response = self.client.get(
  197. url, format="json", data={"field": ["message", "count()"], "statsPeriod": "7d"}
  198. )
  199. assert response.data["eventID"] == "b" * 32
  200. assert response.data["nextEventID"] == "c" * 32, "c is newer & matches message + range"
  201. assert response.data["previousEventID"] == "2" * 32, "d is older & matches message + range"
  202. assert response.data["oldestEventID"] == "2" * 32, "3 is outside range, no match"
  203. assert response.data["latestEventID"] == "c" * 32, "c is newest matching message"
  204. def test_event_links_with_tag_fields(self):
  205. # Create events that overlap with other event messages but
  206. # with different tags
  207. ten_sec_ago = iso_format(before_now(seconds=10))
  208. self.store_event(
  209. data={
  210. "event_id": "2" * 32,
  211. "message": "very bad",
  212. "timestamp": ten_sec_ago,
  213. "tags": {"important": "yes"},
  214. },
  215. project_id=self.project.id,
  216. )
  217. thirty_sec_ago = iso_format(before_now(seconds=30))
  218. self.store_event(
  219. data={
  220. "event_id": "1" * 32,
  221. "message": "very bad",
  222. "timestamp": thirty_sec_ago,
  223. "tags": {"important": "yes"},
  224. },
  225. project_id=self.project.id,
  226. )
  227. five_min_ago = iso_format(before_now(minutes=5))
  228. self.store_event(
  229. data={
  230. "event_id": "d" * 32,
  231. "message": "very bad",
  232. "timestamp": five_min_ago,
  233. "tags": {"important": "no"},
  234. },
  235. project_id=self.project.id,
  236. )
  237. url = reverse(
  238. "sentry-api-0-organization-event-details",
  239. kwargs={
  240. "organization_slug": self.project.organization.slug,
  241. "project_slug": self.project.slug,
  242. "event_id": "1" * 32,
  243. },
  244. )
  245. with self.feature("organizations:events-v2"):
  246. response = self.client.get(url, format="json", data={"field": ["important", "count()"]})
  247. assert response.data["eventID"] == "1" * 32
  248. assert response.data["previousEventID"] is None, "no matching tags"
  249. assert response.data["oldestEventID"] is None, "no older matching events"
  250. assert response.data["nextEventID"] == "2" * 32, "2 is older and has matching tags "
  251. assert response.data["latestEventID"] == "2" * 32, "2 is oldest matching message"
  252. def test_event_links_with_transaction_events(self):
  253. prototype = {
  254. "type": "transaction",
  255. "transaction": "api.issue.delete",
  256. "spans": [],
  257. "contexts": {"trace": {"op": "foobar", "trace_id": "a" * 32, "span_id": "a" * 16}},
  258. "tags": {"important": "yes"},
  259. }
  260. fixtures = (
  261. ("d" * 32, before_now(minutes=1)),
  262. ("e" * 32, before_now(minutes=2)),
  263. ("f" * 32, before_now(minutes=3)),
  264. )
  265. for fixture in fixtures:
  266. data = prototype.copy()
  267. data["event_id"] = fixture[0]
  268. data["timestamp"] = iso_format(fixture[1])
  269. data["start_timestamp"] = iso_format(fixture[1] - timedelta(seconds=5))
  270. self.store_event(data=data, project_id=self.project.id)
  271. url = reverse(
  272. "sentry-api-0-organization-event-details",
  273. kwargs={
  274. "organization_slug": self.project.organization.slug,
  275. "project_slug": self.project.slug,
  276. "event_id": "e" * 32,
  277. },
  278. )
  279. with self.feature("organizations:events-v2"):
  280. response = self.client.get(
  281. url,
  282. format="json",
  283. data={"field": ["important", "count()"], "query": "transaction.duration:>2"},
  284. )
  285. assert response.status_code == 200
  286. assert response.data["nextEventID"] == "d" * 32
  287. assert response.data["previousEventID"] == "f" * 32