test_organization_events.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. from __future__ import absolute_import
  2. from six.moves.urllib.parse import urlencode
  3. from datetime import datetime, timedelta
  4. from django.utils import timezone
  5. from django.core.urlresolvers import reverse
  6. from sentry.testutils import APITestCase, SnubaTestCase
  7. class OrganizationEventsTestBase(APITestCase, SnubaTestCase):
  8. def setUp(self):
  9. super(OrganizationEventsTestBase, self).setUp()
  10. self.min_ago = timezone.now() - timedelta(minutes=1)
  11. self.day_ago = timezone.now() - timedelta(days=1)
  12. class OrganizationEventsEndpointTest(OrganizationEventsTestBase):
  13. def assert_events_in_response(self, response, event_ids):
  14. assert sorted(map(lambda x: x['eventID'], response.data)) == sorted(event_ids)
  15. def test_simple(self):
  16. self.login_as(user=self.user)
  17. project = self.create_project()
  18. project2 = self.create_project()
  19. group = self.create_group(project=project)
  20. group2 = self.create_group(project=project2)
  21. event_1 = self.create_event('a' * 32, group=group, datetime=self.min_ago)
  22. event_2 = self.create_event('b' * 32, group=group2, datetime=self.min_ago)
  23. url = reverse(
  24. 'sentry-api-0-organization-events',
  25. kwargs={
  26. 'organization_slug': project.organization.slug,
  27. }
  28. )
  29. response = self.client.get(url, format='json')
  30. assert response.status_code == 200, response.content
  31. assert len(response.data) == 2
  32. self.assert_events_in_response(response, [event_1.event_id, event_2.event_id])
  33. def test_simple_superuser(self):
  34. user = self.create_user(is_superuser=True)
  35. self.login_as(user=user, superuser=True)
  36. project = self.create_project()
  37. project2 = self.create_project()
  38. group = self.create_group(project=project)
  39. group2 = self.create_group(project=project2)
  40. event_1 = self.create_event('a' * 32, group=group, datetime=self.min_ago)
  41. event_2 = self.create_event('b' * 32, group=group2, datetime=self.min_ago)
  42. url = reverse(
  43. 'sentry-api-0-organization-events',
  44. kwargs={
  45. 'organization_slug': project.organization.slug,
  46. }
  47. )
  48. response = self.client.get(url, format='json')
  49. assert response.status_code == 200, response.content
  50. assert len(response.data) == 2
  51. self.assert_events_in_response(response, [event_1.event_id, event_2.event_id])
  52. def test_message_search_raw_text(self):
  53. self.login_as(user=self.user)
  54. project = self.create_project()
  55. group = self.create_group(project=project)
  56. self.create_event('x' * 32, group=group, message="how to make fast", datetime=self.min_ago)
  57. event_2 = self.create_event(
  58. 'y' * 32,
  59. group=group,
  60. message="Delet the Data",
  61. datetime=self.min_ago,
  62. )
  63. url = reverse(
  64. 'sentry-api-0-organization-events',
  65. kwargs={
  66. 'organization_slug': project.organization.slug,
  67. }
  68. )
  69. response = self.client.get(url, {'query': 'delet'}, format='json')
  70. assert response.status_code == 200, response.content
  71. assert len(response.data) == 1
  72. assert response.data[0]['eventID'] == event_2.event_id
  73. assert response.data[0]['message'] == 'Delet the Data'
  74. def test_message_search_tags(self):
  75. self.login_as(user=self.user)
  76. project = self.create_project()
  77. group = self.create_group(project=project)
  78. event_1 = self.create_event(
  79. 'x' * 32,
  80. group=group,
  81. message="how to make fast",
  82. datetime=self.min_ago,
  83. )
  84. event_2 = self.create_event(
  85. 'y' * 32,
  86. group=group,
  87. message="Delet the Data",
  88. datetime=self.min_ago,
  89. user={'email': 'foo@example.com'},
  90. )
  91. url = reverse(
  92. 'sentry-api-0-organization-events',
  93. kwargs={
  94. 'organization_slug': project.organization.slug,
  95. }
  96. )
  97. response = self.client.get(url, {'query': 'user.email:foo@example.com'}, format='json')
  98. assert response.status_code == 200, response.content
  99. assert len(response.data) == 1
  100. assert response.data[0]['eventID'] == event_2.event_id
  101. assert response.data[0]['message'] == 'Delet the Data'
  102. response = self.client.get(url, {'query': '!user.email:foo@example.com'}, format='json')
  103. assert response.status_code == 200, response.content
  104. assert len(response.data) == 1
  105. assert response.data[0]['eventID'] == event_1.event_id
  106. assert response.data[0]['message'] == 'how to make fast'
  107. def test_invalid_search_terms(self):
  108. self.login_as(user=self.user)
  109. project = self.create_project()
  110. group = self.create_group(project=project)
  111. self.create_event('x' * 32, group=group, message="how to make fast", datetime=self.min_ago)
  112. url = reverse(
  113. 'sentry-api-0-organization-events',
  114. kwargs={
  115. 'organization_slug': project.organization.slug,
  116. }
  117. )
  118. response = self.client.get(url, {'query': 'hi \n there'}, format='json')
  119. assert response.status_code == 400, response.content
  120. assert response.data['detail'] == "Parse error: 'search' (column 1)"
  121. def test_project_filtering(self):
  122. user = self.create_user()
  123. org = self.create_organization()
  124. team = self.create_team(organization=org)
  125. self.create_member(organization=org, user=user, teams=[team])
  126. self.login_as(user=user)
  127. project = self.create_project(organization=org, teams=[team])
  128. project2 = self.create_project(organization=org, teams=[team])
  129. project3 = self.create_project(organization=org)
  130. group = self.create_group(project=project)
  131. group2 = self.create_group(project=project2)
  132. group3 = self.create_group(project=project3)
  133. event_1 = self.create_event('a' * 32, group=group, datetime=self.min_ago)
  134. event_2 = self.create_event('b' * 32, group=group2, datetime=self.min_ago)
  135. self.create_event('c' * 32, group=group3, datetime=self.min_ago)
  136. base_url = reverse(
  137. 'sentry-api-0-organization-events',
  138. kwargs={
  139. 'organization_slug': project.organization.slug,
  140. }
  141. )
  142. # test bad project id
  143. url = '%s?project=abc' % (base_url,)
  144. response = self.client.get(url, format='json')
  145. assert response.status_code == 400
  146. # test including project user doesn't have access to
  147. url = '%s?project=%s&project=%s' % (base_url, project.id, project3.id)
  148. response = self.client.get(url, format='json')
  149. assert response.status_code == 403
  150. # test filtering by project
  151. url = '%s?project=%s' % (base_url, project.id)
  152. response = self.client.get(url, format='json')
  153. assert response.status_code == 200, response.content
  154. assert len(response.data) == 1
  155. self.assert_events_in_response(response, [event_1.event_id])
  156. # test only returns events from project user has access to
  157. response = self.client.get(base_url, format='json')
  158. assert response.status_code == 200, response.content
  159. assert len(response.data) == 2
  160. self.assert_events_in_response(response, [event_1.event_id, event_2.event_id])
  161. def test_stats_period(self):
  162. self.login_as(user=self.user)
  163. project = self.create_project()
  164. project2 = self.create_project()
  165. group = self.create_group(project=project)
  166. group2 = self.create_group(project=project2)
  167. event_1 = self.create_event('a' * 32, group=group, datetime=self.min_ago)
  168. self.create_event('b' * 32, group=group2, datetime=self.day_ago)
  169. url = reverse(
  170. 'sentry-api-0-organization-events',
  171. kwargs={
  172. 'organization_slug': project.organization.slug,
  173. }
  174. )
  175. url = '%s?statsPeriod=2h' % (url,)
  176. response = self.client.get(url, format='json')
  177. assert response.status_code == 200, response.content
  178. assert len(response.data) == 1
  179. self.assert_events_in_response(response, [event_1.event_id])
  180. def test_time_range(self):
  181. self.login_as(user=self.user)
  182. project = self.create_project()
  183. project2 = self.create_project()
  184. group = self.create_group(project=project)
  185. group2 = self.create_group(project=project2)
  186. event_1 = self.create_event('a' * 32, group=group, datetime=self.min_ago)
  187. self.create_event('b' * 32, group=group2, datetime=self.day_ago)
  188. now = timezone.now()
  189. base_url = reverse(
  190. 'sentry-api-0-organization-events',
  191. kwargs={
  192. 'organization_slug': project.organization.slug,
  193. }
  194. )
  195. # test swapped order of start/end
  196. url = '%s?%s' % (base_url, urlencode({
  197. 'end': (now - timedelta(hours=2)).strftime('%Y-%m-%dT%H:%M:%S'),
  198. 'start': now.strftime('%Y-%m-%dT%H:%M:%S'),
  199. }))
  200. response = self.client.get(url, format='json')
  201. assert response.status_code == 400
  202. url = '%s?%s' % (base_url, urlencode({
  203. 'start': (now - timedelta(hours=2)).strftime('%Y-%m-%dT%H:%M:%S'),
  204. 'end': now.strftime('%Y-%m-%dT%H:%M:%S'),
  205. }))
  206. response = self.client.get(url, format='json')
  207. assert response.status_code == 200, response.content
  208. assert len(response.data) == 1
  209. self.assert_events_in_response(response, [event_1.event_id])
  210. def test_environment_filtering(self):
  211. user = self.create_user()
  212. org = self.create_organization()
  213. team = self.create_team(organization=org)
  214. self.create_member(organization=org, user=user, teams=[team])
  215. self.login_as(user=user)
  216. project = self.create_project(organization=org, teams=[team])
  217. environment = self.create_environment(project=project, name="production")
  218. environment2 = self.create_environment(project=project)
  219. null_env = self.create_environment(project=project, name='')
  220. group = self.create_group(project=project)
  221. event_1 = self.create_event(
  222. 'a' * 32, group=group, datetime=self.min_ago, tags={'environment': environment.name}
  223. )
  224. event_2 = self.create_event(
  225. 'b' * 32, group=group, datetime=self.min_ago, tags={'environment': environment.name}
  226. )
  227. event_3 = self.create_event(
  228. 'c' * 32, group=group, datetime=self.min_ago, tags={'environment': environment2.name}
  229. )
  230. event_4 = self.create_event(
  231. 'd' * 32, group=group, datetime=self.min_ago,
  232. )
  233. base_url = reverse(
  234. 'sentry-api-0-organization-events',
  235. kwargs={
  236. 'organization_slug': org.slug,
  237. }
  238. )
  239. # test as part of query param
  240. url = '%s?environment=%s' % (base_url, environment.name)
  241. response = self.client.get(url, format='json')
  242. assert response.status_code == 200, response.content
  243. assert len(response.data) == 2
  244. self.assert_events_in_response(response, [event_1.event_id, event_2.event_id])
  245. # test multiple as part of query param
  246. url = '%s?%s' % (base_url, urlencode((
  247. ('environment', environment.name),
  248. ('environment', environment2.name),
  249. )))
  250. response = self.client.get(url, format='json')
  251. assert response.status_code == 200, response.content
  252. assert len(response.data) == 3
  253. self.assert_events_in_response(
  254. response, [event_1.event_id, event_2.event_id, event_3.event_id])
  255. # test multiple as part of query param with no env
  256. url = '%s?%s' % (base_url, urlencode((
  257. ('environment', environment.name),
  258. ('environment', null_env.name),
  259. )))
  260. response = self.client.get(url, format='json')
  261. assert response.status_code == 200, response.content
  262. assert len(response.data) == 3
  263. self.assert_events_in_response(
  264. response, [event_1.event_id, event_2.event_id, event_4.event_id])
  265. # test as part of search
  266. url = '%s?query=environment:%s' % (base_url, environment.name)
  267. response = self.client.get(url, format='json')
  268. assert response.status_code == 200, response.content
  269. assert len(response.data) == 2
  270. self.assert_events_in_response(response, [event_1.event_id, event_2.event_id])
  271. # test as part of search - no environment
  272. url = '%s?query=environment:""' % (base_url, )
  273. response = self.client.get(url, format='json')
  274. assert response.status_code == 200, response.content
  275. assert len(response.data) == 1
  276. self.assert_events_in_response(response, [event_4.event_id])
  277. # test nonexistent environment
  278. url = '%s?environment=notanenvironment' % (base_url,)
  279. response = self.client.get(url, format='json')
  280. assert response.status_code == 404
  281. def test_custom_tags(self):
  282. user = self.create_user()
  283. org = self.create_organization()
  284. team = self.create_team(organization=org)
  285. self.create_member(organization=org, user=user, teams=[team])
  286. self.login_as(user=user)
  287. project = self.create_project(organization=org, teams=[team])
  288. group = self.create_group(project=project)
  289. event_1 = self.create_event(
  290. 'a' * 32, group=group, datetime=self.min_ago, tags={'fruit': 'apple'}
  291. )
  292. event_2 = self.create_event(
  293. 'b' * 32, group=group, datetime=self.min_ago, tags={'fruit': 'orange'}
  294. )
  295. base_url = reverse(
  296. 'sentry-api-0-organization-events',
  297. kwargs={
  298. 'organization_slug': org.slug,
  299. }
  300. )
  301. response = self.client.get('%s?query=fruit:apple' % (base_url,), format='json')
  302. assert response.status_code == 200, response.content
  303. assert len(response.data) == 1
  304. self.assert_events_in_response(response, [event_1.event_id])
  305. response = self.client.get('%s?query=!fruit:apple' % (base_url,), format='json')
  306. assert response.status_code == 200, response.content
  307. assert len(response.data) == 1
  308. self.assert_events_in_response(response, [event_2.event_id])
  309. def test_wildcard_search(self):
  310. user = self.create_user()
  311. org = self.create_organization()
  312. team = self.create_team(organization=org)
  313. self.create_member(organization=org, user=user, teams=[team])
  314. self.login_as(user=user)
  315. project = self.create_project(organization=org, teams=[team])
  316. group = self.create_group(project=project)
  317. event_1 = self.create_event(
  318. 'a' * 32, group=group, datetime=self.min_ago, tags={'sentry:release': '3.1.2'}
  319. )
  320. event_2 = self.create_event(
  321. 'b' * 32, group=group, datetime=self.min_ago, tags={'sentry:release': '4.1.2'}
  322. )
  323. event_3 = self.create_event(
  324. 'c' * 32, group=group, datetime=self.min_ago, user={'email': 'foo@example.com'}
  325. )
  326. event_4 = self.create_event(
  327. 'd' * 32, group=group, datetime=self.min_ago, user={'email': 'foo@example.commmmmmmm'}
  328. )
  329. base_url = reverse(
  330. 'sentry-api-0-organization-events',
  331. kwargs={
  332. 'organization_slug': org.slug,
  333. }
  334. )
  335. response = self.client.get('%s?query=release:3.1.*' % (base_url,), format='json')
  336. assert response.status_code == 200, response.content
  337. assert len(response.data) == 1
  338. self.assert_events_in_response(response, [event_1.event_id])
  339. response = self.client.get('%s?query=!release:3.1.*' % (base_url,), format='json')
  340. assert response.status_code == 200, response.content
  341. assert len(response.data) == 3
  342. self.assert_events_in_response(
  343. response,
  344. [event_2.event_id, event_3.event_id, event_4.event_id],
  345. )
  346. response = self.client.get('%s?query=user.email:*@example.com' % (base_url,), format='json')
  347. assert response.status_code == 200, response.content
  348. assert len(response.data) == 1
  349. self.assert_events_in_response(response, [event_3.event_id])
  350. response = self.client.get(
  351. '%s?query=!user.email:*@example.com' % (base_url,),
  352. format='json',
  353. )
  354. assert response.status_code == 200, response.content
  355. assert len(response.data) == 3
  356. self.assert_events_in_response(
  357. response,
  358. [event_1.event_id, event_2.event_id, event_4.event_id],
  359. )
  360. def test_has_tag(self):
  361. user = self.create_user()
  362. org = self.create_organization()
  363. team = self.create_team(organization=org)
  364. self.create_member(organization=org, user=user, teams=[team])
  365. self.login_as(user=user)
  366. project = self.create_project(organization=org, teams=[team])
  367. group = self.create_group(project=project)
  368. event_1 = self.create_event(
  369. 'a' * 32, group=group, datetime=self.min_ago, user={'email': 'foo@example.com'},
  370. )
  371. event_2 = self.create_event(
  372. 'b' * 32,
  373. group=group,
  374. datetime=self.min_ago,
  375. tags={
  376. 'example_tag': 'example_value'})
  377. base_url = reverse(
  378. 'sentry-api-0-organization-events',
  379. kwargs={
  380. 'organization_slug': org.slug,
  381. }
  382. )
  383. response = self.client.get('%s?query=has:user.email' % (base_url,), format='json')
  384. assert response.status_code == 200, response.content
  385. assert len(response.data) == 1
  386. self.assert_events_in_response(response, [event_1.event_id])
  387. # test custom tag
  388. response = self.client.get('%s?query=has:example_tag' % (base_url,), format='json')
  389. assert response.status_code == 200, response.content
  390. assert len(response.data) == 1
  391. self.assert_events_in_response(response, [event_2.event_id])
  392. response = self.client.get('%s?query=!has:user.email' % (base_url,), format='json')
  393. assert response.status_code == 200, response.content
  394. assert len(response.data) == 1
  395. self.assert_events_in_response(response, [event_2.event_id])
  396. # test custom tag
  397. response = self.client.get('%s?query=!has:example_tag' % (base_url,), format='json')
  398. assert response.status_code == 200, response.content
  399. assert len(response.data) == 1
  400. self.assert_events_in_response(response, [event_1.event_id])
  401. def test_no_projects(self):
  402. org = self.create_organization(owner=self.user)
  403. self.login_as(user=self.user)
  404. url = reverse(
  405. 'sentry-api-0-organization-events',
  406. kwargs={
  407. 'organization_slug': org.slug,
  408. }
  409. )
  410. response = self.client.get(url, format='json')
  411. assert response.status_code == 200, response.content
  412. assert len(response.data) == 0
  413. class OrganizationEventsStatsEndpointTest(OrganizationEventsTestBase):
  414. def test_simple(self):
  415. self.login_as(user=self.user)
  416. project = self.create_project()
  417. project2 = self.create_project()
  418. group = self.create_group(project=project)
  419. group2 = self.create_group(project=project2)
  420. self.create_event(
  421. 'a' * 32,
  422. group=group,
  423. datetime=datetime(
  424. 2018,
  425. 11,
  426. 1,
  427. 10,
  428. 59,
  429. 00,
  430. tzinfo=timezone.utc))
  431. self.create_event(
  432. 'b' * 32,
  433. group=group2,
  434. datetime=datetime(
  435. 2018,
  436. 11,
  437. 1,
  438. 11,
  439. 30,
  440. 00,
  441. tzinfo=timezone.utc))
  442. self.create_event(
  443. 'c' * 32,
  444. group=group2,
  445. datetime=datetime(
  446. 2018,
  447. 11,
  448. 1,
  449. 11,
  450. 45,
  451. 00,
  452. tzinfo=timezone.utc))
  453. url = reverse(
  454. 'sentry-api-0-organization-events-stats',
  455. kwargs={
  456. 'organization_slug': project.organization.slug,
  457. }
  458. )
  459. response = self.client.get('%s?%s' % (url, urlencode({
  460. 'start': '2018-11-01T10:00:00',
  461. 'end': '2018-11-01T11:59:00',
  462. 'interval': '1h',
  463. })), format='json')
  464. assert response.status_code == 200, response.content
  465. assert response.data['data'] == [
  466. (1541062800, []),
  467. (1541066400, [{'count': 1}]),
  468. (1541070000, [{'count': 2}]),
  469. ]
  470. def test_no_projects(self):
  471. org = self.create_organization(owner=self.user)
  472. self.login_as(user=self.user)
  473. url = reverse(
  474. 'sentry-api-0-organization-events-stats',
  475. kwargs={
  476. 'organization_slug': org.slug,
  477. }
  478. )
  479. response = self.client.get(url, format='json')
  480. assert response.status_code == 200, response.content
  481. assert len(response.data['data']) == 0
  482. class OrganizationEventsMetaEndpoint(OrganizationEventsTestBase):
  483. def test_simple(self):
  484. self.login_as(user=self.user)
  485. project = self.create_project()
  486. project2 = self.create_project()
  487. group = self.create_group(project=project)
  488. group2 = self.create_group(project=project2)
  489. self.create_event('a' * 32, group=group, datetime=self.min_ago)
  490. self.create_event('m' * 32, group=group2, datetime=self.min_ago)
  491. url = reverse(
  492. 'sentry-api-0-organization-events-meta',
  493. kwargs={
  494. 'organization_slug': project.organization.slug,
  495. }
  496. )
  497. response = self.client.get(url, format='json')
  498. assert response.status_code == 200, response.content
  499. # this is not exact because of turbo=True
  500. assert response.data['count'] == 10
  501. def test_search(self):
  502. self.login_as(user=self.user)
  503. project = self.create_project()
  504. group = self.create_group(project=project)
  505. self.create_event('x' * 32, group=group, message="how to make fast", datetime=self.min_ago)
  506. self.create_event(
  507. 'm' * 32,
  508. group=group,
  509. message="Delet the Data",
  510. datetime=self.min_ago,
  511. )
  512. url = reverse(
  513. 'sentry-api-0-organization-events-meta',
  514. kwargs={
  515. 'organization_slug': project.organization.slug,
  516. }
  517. )
  518. response = self.client.get(url, {'query': 'delet'}, format='json')
  519. assert response.status_code == 200, response.content
  520. # this is not exact because of turbo=True
  521. assert response.data['count'] == 10
  522. def test_no_projects(self):
  523. org = self.create_organization(owner=self.user)
  524. self.login_as(user=self.user)
  525. url = reverse(
  526. 'sentry-api-0-organization-events-meta',
  527. kwargs={
  528. 'organization_slug': org.slug,
  529. }
  530. )
  531. response = self.client.get(url, format='json')
  532. assert response.status_code == 200, response.content
  533. assert response.data['count'] == 0