test_organization_events_v2.py 87 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336
  1. from __future__ import absolute_import
  2. import six
  3. import random
  4. import mock
  5. from pytz import utc
  6. from datetime import timedelta
  7. from math import ceil
  8. from django.core.urlresolvers import reverse
  9. from sentry.testutils import APITestCase, SnubaTestCase
  10. from sentry.testutils.helpers import parse_link_header
  11. from sentry.testutils.helpers.datetime import before_now, iso_format
  12. from sentry.utils.samples import load_data
  13. from sentry.utils.compat.mock import patch
  14. from sentry.utils.snuba import (
  15. RateLimitExceeded,
  16. QueryIllegalTypeOfArgument,
  17. QueryExecutionError,
  18. )
  19. class OrganizationEventsV2EndpointTest(APITestCase, SnubaTestCase):
  20. def setUp(self):
  21. super(OrganizationEventsV2EndpointTest, self).setUp()
  22. self.min_ago = iso_format(before_now(minutes=1))
  23. self.two_min_ago = iso_format(before_now(minutes=2))
  24. def do_request(self, query, features=None):
  25. if features is None:
  26. features = {"organizations:discover-basic": True}
  27. self.login_as(user=self.user)
  28. url = reverse(
  29. "sentry-api-0-organization-eventsv2",
  30. kwargs={"organization_slug": self.organization.slug},
  31. )
  32. with self.feature(features):
  33. return self.client.get(url, query, format="json")
  34. def test_no_projects(self):
  35. response = self.do_request({})
  36. assert response.status_code == 200, response.content
  37. assert len(response.data) == 0
  38. def test_performance_view_feature(self):
  39. self.store_event(
  40. data={"event_id": "a" * 32, "timestamp": self.min_ago, "fingerprint": ["group1"]},
  41. project_id=self.project.id,
  42. )
  43. query = {"field": ["id", "project.id"], "project": [self.project.id]}
  44. response = self.do_request(query)
  45. assert response.status_code == 200
  46. assert len(response.data["data"]) == 1
  47. def test_multi_project_feature_gate_rejection(self):
  48. team = self.create_team(organization=self.organization, members=[self.user])
  49. project = self.create_project(organization=self.organization, teams=[team])
  50. project2 = self.create_project(organization=self.organization, teams=[team])
  51. self.store_event(
  52. data={"event_id": "a" * 32, "timestamp": self.min_ago, "fingerprint": ["group1"]},
  53. project_id=project.id,
  54. )
  55. self.store_event(
  56. data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group2"]},
  57. project_id=project2.id,
  58. )
  59. query = {"field": ["id", "project.id"], "project": [project.id, project2.id]}
  60. response = self.do_request(query)
  61. assert response.status_code == 400
  62. assert "events from multiple projects" in response.data["detail"]
  63. def test_invalid_search_terms(self):
  64. project = self.create_project()
  65. self.store_event(
  66. data={"event_id": "a" * 32, "message": "how to make fast", "timestamp": self.min_ago},
  67. project_id=project.id,
  68. )
  69. query = {"field": ["id"], "query": "hi \n there"}
  70. response = self.do_request(query)
  71. assert response.status_code == 400, response.content
  72. assert (
  73. response.data["detail"]
  74. == "Parse error at 'hi \n ther' (column 4). This is commonly caused by unmatched parentheses. Enclose any text in double quotes."
  75. )
  76. @patch("sentry.snuba.discover.raw_query")
  77. def test_handling_snuba_errors(self, mock_query):
  78. mock_query.side_effect = RateLimitExceeded("test")
  79. project = self.create_project()
  80. self.store_event(
  81. data={"event_id": "a" * 32, "message": "how to make fast"}, project_id=project.id
  82. )
  83. query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
  84. response = self.do_request(query)
  85. assert response.status_code == 400, response.content
  86. assert (
  87. response.data["detail"]
  88. == "Query timeout. Please try again. If the problem persists try a smaller date range or fewer projects."
  89. )
  90. mock_query.side_effect = QueryExecutionError("test")
  91. query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
  92. response = self.do_request(query)
  93. assert response.status_code == 400, response.content
  94. assert response.data["detail"] == "Internal error. Your query failed to run."
  95. mock_query.side_effect = QueryIllegalTypeOfArgument("test")
  96. query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
  97. response = self.do_request(query)
  98. assert response.status_code == 400, response.content
  99. assert response.data["detail"] == "Invalid query. Argument to function is wrong type."
  100. def test_out_of_retention(self):
  101. self.create_project()
  102. with self.options({"system.event-retention-days": 10}):
  103. query = {
  104. "field": ["id", "timestamp"],
  105. "orderby": ["-timestamp", "-id"],
  106. "start": iso_format(before_now(days=20)),
  107. "end": iso_format(before_now(days=15)),
  108. }
  109. response = self.do_request(query)
  110. assert response.status_code == 400, response.content
  111. assert response.data["detail"] == "Invalid date range. Please try a more recent date range."
  112. def test_raw_data(self):
  113. project = self.create_project()
  114. self.store_event(
  115. data={
  116. "event_id": "a" * 32,
  117. "environment": "staging",
  118. "timestamp": self.two_min_ago,
  119. "user": {"ip_address": "127.0.0.1", "email": "foo@example.com"},
  120. },
  121. project_id=project.id,
  122. )
  123. self.store_event(
  124. data={
  125. "event_id": "b" * 32,
  126. "environment": "staging",
  127. "timestamp": self.min_ago,
  128. "user": {"ip_address": "127.0.0.1", "email": "foo@example.com"},
  129. },
  130. project_id=project.id,
  131. )
  132. query = {
  133. "field": ["id", "project.id", "user.email", "user.ip", "timestamp"],
  134. "orderby": "-timestamp",
  135. }
  136. response = self.do_request(query)
  137. assert response.status_code == 200, response.content
  138. data = response.data["data"]
  139. assert len(data) == 2
  140. assert data[0]["id"] == "b" * 32
  141. assert data[0]["project.id"] == project.id
  142. assert data[0]["user.email"] == "foo@example.com"
  143. assert "project.name" not in data[0], "project.id does not auto select name"
  144. assert "project" not in data[0]
  145. meta = response.data["meta"]
  146. assert meta["id"] == "string"
  147. assert meta["user.email"] == "string"
  148. assert meta["user.ip"] == "string"
  149. assert meta["timestamp"] == "date"
  150. def test_project_name(self):
  151. project = self.create_project()
  152. self.store_event(
  153. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  154. project_id=project.id,
  155. )
  156. query = {"field": ["project.name", "environment"]}
  157. response = self.do_request(query)
  158. assert response.status_code == 200, response.content
  159. assert len(response.data["data"]) == 1
  160. assert response.data["data"][0]["project.name"] == project.slug
  161. assert "project.id" not in response.data["data"][0]
  162. assert response.data["data"][0]["environment"] == "staging"
  163. def test_project_without_name(self):
  164. project = self.create_project()
  165. self.store_event(
  166. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  167. project_id=project.id,
  168. )
  169. query = {"field": ["project", "environment"]}
  170. response = self.do_request(query)
  171. assert response.status_code == 200, response.content
  172. assert len(response.data["data"]) == 1
  173. assert response.data["data"][0]["project"] == project.slug
  174. assert response.data["meta"]["project"] == "string"
  175. assert "project.id" not in response.data["data"][0]
  176. assert response.data["data"][0]["environment"] == "staging"
  177. def test_project_in_query(self):
  178. project = self.create_project()
  179. self.store_event(
  180. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  181. project_id=project.id,
  182. )
  183. query = {
  184. "field": ["project", "count()"],
  185. "query": 'project:"%s"' % project.slug,
  186. "statsPeriod": "14d",
  187. }
  188. response = self.do_request(query)
  189. assert response.status_code == 200, response.content
  190. assert len(response.data["data"]) == 1
  191. assert response.data["data"][0]["project"] == project.slug
  192. assert "project.id" not in response.data["data"][0]
  193. def test_project_in_query_not_in_header(self):
  194. project = self.create_project()
  195. other_project = self.create_project()
  196. self.store_event(
  197. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  198. project_id=project.id,
  199. )
  200. query = {
  201. "field": ["project", "count()"],
  202. "query": 'project:"%s"' % project.slug,
  203. "statsPeriod": "14d",
  204. "project": other_project.id,
  205. }
  206. response = self.do_request(query)
  207. assert response.status_code == 400, response.content
  208. assert (
  209. response.data["detail"]
  210. == "Invalid query. Project %s does not exist or is not an actively selected project."
  211. % project.slug
  212. )
  213. def test_project_in_query_does_not_exist(self):
  214. project = self.create_project()
  215. self.store_event(
  216. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  217. project_id=project.id,
  218. )
  219. query = {
  220. "field": ["project", "count()"],
  221. "query": "project:morty",
  222. "statsPeriod": "14d",
  223. }
  224. response = self.do_request(query)
  225. assert response.status_code == 400, response.content
  226. assert (
  227. response.data["detail"]
  228. == "Invalid query. Project morty does not exist or is not an actively selected project."
  229. )
  230. def test_project_condition_used_for_automatic_filters(self):
  231. project = self.create_project()
  232. self.store_event(
  233. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  234. project_id=project.id,
  235. )
  236. query = {
  237. "field": ["project", "count()"],
  238. "query": 'project:"%s"' % project.slug,
  239. "statsPeriod": "14d",
  240. }
  241. response = self.do_request(query)
  242. assert response.status_code == 200, response.content
  243. assert len(response.data["data"]) == 1
  244. assert response.data["data"][0]["project"] == project.slug
  245. assert "project.id" not in response.data["data"][0]
  246. def test_user_search(self):
  247. project = self.create_project()
  248. data = load_data("transaction", timestamp=before_now(minutes=1))
  249. data["user"] = {
  250. "email": "foo@example.com",
  251. "id": "123",
  252. "ip_address": "127.0.0.1",
  253. "username": "foo",
  254. }
  255. self.store_event(data, project_id=project.id)
  256. fields = {
  257. "email": "user.email",
  258. "id": "user.id",
  259. "ip_address": "user.ip",
  260. "username": "user.username",
  261. }
  262. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  263. for key, value in data["user"].items():
  264. field = fields[key]
  265. query = {
  266. "field": ["project", "user"],
  267. "query": "{}:{}".format(field, value),
  268. "statsPeriod": "14d",
  269. }
  270. response = self.do_request(query, features=features)
  271. assert response.status_code == 200, response.content
  272. assert len(response.data["data"]) == 1
  273. assert response.data["data"][0]["project"] == project.slug
  274. assert response.data["data"][0]["user"] == "id:123"
  275. def test_has_user(self):
  276. project = self.create_project()
  277. data = load_data("transaction", timestamp=before_now(minutes=1))
  278. self.store_event(data, project_id=project.id)
  279. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  280. for value in data["user"].values():
  281. query = {"field": ["project", "user"], "query": "has:user", "statsPeriod": "14d"}
  282. response = self.do_request(query, features=features)
  283. assert response.status_code == 200, response.content
  284. assert len(response.data["data"]) == 1
  285. assert response.data["data"][0]["user"] == "ip:{}".format(data["user"]["ip_address"])
  286. def test_has_issue(self):
  287. project = self.create_project()
  288. event = self.store_event(
  289. {"timestamp": iso_format(before_now(minutes=1))}, project_id=project.id
  290. )
  291. data = load_data("transaction", timestamp=before_now(minutes=1))
  292. self.store_event(data, project_id=project.id)
  293. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  294. query = {"field": ["project", "issue"], "query": "has:issue", "statsPeriod": "14d"}
  295. response = self.do_request(query, features=features)
  296. assert response.status_code == 200, response.content
  297. assert len(response.data["data"]) == 1
  298. assert response.data["data"][0]["issue"] == event.group.qualified_short_id
  299. query = {"field": ["project", "issue"], "query": "!has:issue", "statsPeriod": "14d"}
  300. response = self.do_request(query, features=features)
  301. assert response.status_code == 200, response.content
  302. assert len(response.data["data"]) == 1
  303. assert response.data["data"][0]["issue"] == "unknown"
  304. def test_negative_user_search(self):
  305. project = self.create_project()
  306. user_data = {"email": "foo@example.com", "id": "123", "username": "foo"}
  307. # Load an event with data that shouldn't match
  308. data = load_data("transaction", timestamp=before_now(minutes=1))
  309. data["transaction"] = "/transactions/nomatch"
  310. event_user = user_data.copy()
  311. event_user["id"] = "undefined"
  312. data["user"] = event_user
  313. self.store_event(data, project_id=project.id)
  314. # Load a matching event
  315. data = load_data("transaction", timestamp=before_now(minutes=1))
  316. data["transaction"] = "/transactions/matching"
  317. data["user"] = user_data
  318. self.store_event(data, project_id=project.id)
  319. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  320. query = {
  321. "field": ["project", "user"],
  322. "query": '!user:"id:undefined"',
  323. "statsPeriod": "14d",
  324. }
  325. response = self.do_request(query, features=features)
  326. assert response.status_code == 200, response.content
  327. assert len(response.data["data"]) == 1
  328. assert response.data["data"][0]["user"] == "id:{}".format(user_data["id"])
  329. assert "user.email" not in response.data["data"][0]
  330. assert "user.id" not in response.data["data"][0]
  331. def test_not_project_in_query(self):
  332. project1 = self.create_project()
  333. project2 = self.create_project()
  334. self.store_event(
  335. data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
  336. project_id=project1.id,
  337. )
  338. self.store_event(
  339. data={"event_id": "b" * 32, "environment": "staging", "timestamp": self.min_ago},
  340. project_id=project2.id,
  341. )
  342. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  343. query = {
  344. "field": ["project", "count()"],
  345. "query": '!project:"%s"' % project1.slug,
  346. "statsPeriod": "14d",
  347. }
  348. response = self.do_request(query, features=features)
  349. assert response.status_code == 200, response.content
  350. assert len(response.data["data"]) == 1
  351. assert response.data["data"][0]["project"] == project2.slug
  352. assert "project.id" not in response.data["data"][0]
  353. def test_implicit_groupby(self):
  354. project = self.create_project()
  355. self.store_event(
  356. data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
  357. project_id=project.id,
  358. )
  359. event1 = self.store_event(
  360. data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group_1"]},
  361. project_id=project.id,
  362. )
  363. event2 = self.store_event(
  364. data={"event_id": "c" * 32, "timestamp": self.min_ago, "fingerprint": ["group_2"]},
  365. project_id=project.id,
  366. )
  367. query = {"field": ["count(id)", "project.id", "issue.id"], "orderby": "issue.id"}
  368. response = self.do_request(query)
  369. assert response.status_code == 200, response.content
  370. assert len(response.data["data"]) == 2
  371. data = response.data["data"]
  372. assert data[0] == {
  373. "project.id": project.id,
  374. "issue.id": event1.group_id,
  375. "count_id": 2,
  376. }
  377. assert data[1] == {
  378. "project.id": project.id,
  379. "issue.id": event2.group_id,
  380. "count_id": 1,
  381. }
  382. meta = response.data["meta"]
  383. assert meta["count_id"] == "integer"
  384. def test_orderby(self):
  385. project = self.create_project()
  386. self.store_event(
  387. data={"event_id": "a" * 32, "timestamp": self.two_min_ago}, project_id=project.id
  388. )
  389. self.store_event(
  390. data={"event_id": "b" * 32, "timestamp": self.min_ago}, project_id=project.id
  391. )
  392. self.store_event(
  393. data={"event_id": "c" * 32, "timestamp": self.min_ago}, project_id=project.id
  394. )
  395. query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
  396. response = self.do_request(query)
  397. assert response.status_code == 200, response.content
  398. data = response.data["data"]
  399. assert data[0]["id"] == "c" * 32
  400. assert data[1]["id"] == "b" * 32
  401. assert data[2]["id"] == "a" * 32
  402. def test_sort_title(self):
  403. project = self.create_project()
  404. self.store_event(
  405. data={"event_id": "a" * 32, "message": "zlast", "timestamp": self.two_min_ago},
  406. project_id=project.id,
  407. )
  408. self.store_event(
  409. data={"event_id": "b" * 32, "message": "second", "timestamp": self.min_ago},
  410. project_id=project.id,
  411. )
  412. self.store_event(
  413. data={"event_id": "c" * 32, "message": "first", "timestamp": self.min_ago},
  414. project_id=project.id,
  415. )
  416. query = {"field": ["id", "title"], "sort": "title"}
  417. response = self.do_request(query)
  418. assert response.status_code == 200, response.content
  419. data = response.data["data"]
  420. assert data[0]["id"] == "c" * 32
  421. assert data[1]["id"] == "b" * 32
  422. assert data[2]["id"] == "a" * 32
  423. def test_sort_invalid(self):
  424. project = self.create_project()
  425. self.store_event(
  426. data={"event_id": "a" * 32, "timestamp": self.two_min_ago}, project_id=project.id
  427. )
  428. query = {"field": ["id"], "sort": "garbage"}
  429. response = self.do_request(query)
  430. assert response.status_code == 400
  431. assert "order by" in response.content
  432. def test_latest_release_alias(self):
  433. project = self.create_project()
  434. event1 = self.store_event(
  435. data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "release": "0.8"},
  436. project_id=project.id,
  437. )
  438. query = {"field": ["issue.id", "release"], "query": "release:latest"}
  439. response = self.do_request(query)
  440. assert response.status_code == 200, response.content
  441. data = response.data["data"]
  442. assert data[0]["issue.id"] == event1.group_id
  443. assert data[0]["release"] == "0.8"
  444. event2 = self.store_event(
  445. data={"event_id": "a" * 32, "timestamp": self.min_ago, "release": "0.9"},
  446. project_id=project.id,
  447. )
  448. query = {"field": ["issue.id", "release"], "query": "release:latest"}
  449. response = self.do_request(query)
  450. assert response.status_code == 200, response.content
  451. data = response.data["data"]
  452. assert data[0]["issue.id"] == event2.group_id
  453. assert data[0]["release"] == "0.9"
  454. def test_aliased_fields(self):
  455. project = self.create_project()
  456. event1 = self.store_event(
  457. data={
  458. "event_id": "a" * 32,
  459. "timestamp": self.min_ago,
  460. "fingerprint": ["group_1"],
  461. "user": {"email": "foo@example.com"},
  462. },
  463. project_id=project.id,
  464. )
  465. event2 = self.store_event(
  466. data={
  467. "event_id": "b" * 32,
  468. "timestamp": self.min_ago,
  469. "fingerprint": ["group_2"],
  470. "user": {"email": "foo@example.com"},
  471. },
  472. project_id=project.id,
  473. )
  474. self.store_event(
  475. data={
  476. "event_id": "c" * 32,
  477. "timestamp": self.min_ago,
  478. "fingerprint": ["group_2"],
  479. "user": {"email": "bar@example.com"},
  480. },
  481. project_id=project.id,
  482. )
  483. query = {
  484. "field": ["issue.id", "count(id)", "count_unique(user)"],
  485. "orderby": "issue.id",
  486. }
  487. response = self.do_request(query)
  488. assert response.status_code == 200, response.content
  489. assert len(response.data["data"]) == 2
  490. data = response.data["data"]
  491. assert data[0]["issue.id"] == event1.group_id
  492. assert data[0]["count_id"] == 1
  493. assert data[0]["count_unique_user"] == 1
  494. assert "projectid" not in data[0]
  495. assert "project.id" not in data[0]
  496. assert data[1]["issue.id"] == event2.group_id
  497. assert data[1]["count_id"] == 2
  498. assert data[1]["count_unique_user"] == 2
  499. def test_aggregate_field_with_dotted_param(self):
  500. project = self.create_project()
  501. event1 = self.store_event(
  502. data={
  503. "event_id": "a" * 32,
  504. "timestamp": self.min_ago,
  505. "fingerprint": ["group_1"],
  506. "user": {"id": "123", "email": "foo@example.com"},
  507. },
  508. project_id=project.id,
  509. )
  510. event2 = self.store_event(
  511. data={
  512. "event_id": "b" * 32,
  513. "timestamp": self.min_ago,
  514. "fingerprint": ["group_2"],
  515. "user": {"id": "123", "email": "foo@example.com"},
  516. },
  517. project_id=project.id,
  518. )
  519. self.store_event(
  520. data={
  521. "event_id": "c" * 32,
  522. "timestamp": self.min_ago,
  523. "fingerprint": ["group_2"],
  524. "user": {"id": "456", "email": "bar@example.com"},
  525. },
  526. project_id=project.id,
  527. )
  528. query = {
  529. "field": ["issue.id", "issue_title", "count(id)", "count_unique(user.email)"],
  530. "orderby": "issue.id",
  531. }
  532. response = self.do_request(query)
  533. assert response.status_code == 200, response.content
  534. assert len(response.data["data"]) == 2
  535. data = response.data["data"]
  536. assert data[0]["issue.id"] == event1.group_id
  537. assert data[0]["count_id"] == 1
  538. assert data[0]["count_unique_user_email"] == 1
  539. assert "projectid" not in data[0]
  540. assert "project.id" not in data[0]
  541. assert data[1]["issue.id"] == event2.group_id
  542. assert data[1]["count_id"] == 2
  543. assert data[1]["count_unique_user_email"] == 2
  544. def test_failure_rate_alias_field(self):
  545. project = self.create_project()
  546. data = load_data("transaction", timestamp=before_now(minutes=1))
  547. data["transaction"] = "/failure_rate/success"
  548. self.store_event(data, project_id=project.id)
  549. data = load_data("transaction", timestamp=before_now(minutes=1))
  550. data["transaction"] = "/failure_rate/unknown"
  551. data["contexts"]["trace"]["status"] = "unknown_error"
  552. self.store_event(data, project_id=project.id)
  553. for i in range(6):
  554. data = load_data("transaction", timestamp=before_now(minutes=1))
  555. data["transaction"] = "/failure_rate/{}".format(i)
  556. data["contexts"]["trace"]["status"] = "unauthenticated"
  557. self.store_event(data, project_id=project.id)
  558. query = {"field": ["failure_rate()"], "query": "event.type:transaction"}
  559. response = self.do_request(query)
  560. assert response.status_code == 200, response.content
  561. assert len(response.data["data"]) == 1
  562. data = response.data["data"]
  563. assert data[0]["failure_rate"] == 0.75
  564. def test_user_misery_alias_field(self):
  565. project = self.create_project()
  566. events = [
  567. ("one", 300),
  568. ("one", 300),
  569. ("two", 3000),
  570. ("two", 3000),
  571. ("three", 300),
  572. ("three", 3000),
  573. ]
  574. for idx, event in enumerate(events):
  575. data = load_data(
  576. "transaction",
  577. timestamp=before_now(minutes=(1 + idx)),
  578. start_timestamp=before_now(minutes=(1 + idx), milliseconds=event[1]),
  579. )
  580. data["event_id"] = "{}".format(idx) * 32
  581. data["transaction"] = "/user_misery/horribilis/{}".format(idx)
  582. data["user"] = {"email": "{}@example.com".format(event[0])}
  583. self.store_event(data, project_id=project.id)
  584. query = {"field": ["user_misery(300)"], "query": "event.type:transaction"}
  585. response = self.do_request(query)
  586. assert response.status_code == 200, response.content
  587. assert len(response.data["data"]) == 1
  588. data = response.data["data"]
  589. assert data[0]["user_misery_300"] == 2
  590. def test_aggregation(self):
  591. project = self.create_project()
  592. self.store_event(
  593. data={
  594. "event_id": "a" * 32,
  595. "timestamp": self.min_ago,
  596. "fingerprint": ["group_1"],
  597. "user": {"email": "foo@example.com"},
  598. "environment": "prod",
  599. "tags": {"sub_customer.is-Enterprise-42": "1"},
  600. },
  601. project_id=project.id,
  602. )
  603. self.store_event(
  604. data={
  605. "event_id": "b" * 32,
  606. "timestamp": self.min_ago,
  607. "fingerprint": ["group_2"],
  608. "user": {"email": "foo@example.com"},
  609. "environment": "staging",
  610. "tags": {"sub_customer.is-Enterprise-42": "1"},
  611. },
  612. project_id=project.id,
  613. )
  614. self.store_event(
  615. data={
  616. "event_id": "c" * 32,
  617. "timestamp": self.min_ago,
  618. "fingerprint": ["group_2"],
  619. "user": {"email": "foo@example.com"},
  620. "environment": "prod",
  621. "tags": {"sub_customer.is-Enterprise-42": "0"},
  622. },
  623. project_id=project.id,
  624. )
  625. self.store_event(
  626. data={
  627. "event_id": "d" * 32,
  628. "timestamp": self.min_ago,
  629. "fingerprint": ["group_2"],
  630. "user": {"email": "foo@example.com"},
  631. "environment": "prod",
  632. "tags": {"sub_customer.is-Enterprise-42": "1"},
  633. },
  634. project_id=project.id,
  635. )
  636. query = {
  637. "field": ["sub_customer.is-Enterprise-42", "count(sub_customer.is-Enterprise-42)"],
  638. "orderby": "sub_customer.is-Enterprise-42",
  639. }
  640. response = self.do_request(query)
  641. assert response.status_code == 200, response.content
  642. assert len(response.data["data"]) == 2
  643. data = response.data["data"]
  644. assert data[0]["count_sub_customer_is-Enterprise-42"] == 1
  645. assert data[1]["count_sub_customer_is-Enterprise-42"] == 3
  646. def test_aggregation_comparison(self):
  647. project = self.create_project()
  648. self.store_event(
  649. data={
  650. "event_id": "a" * 32,
  651. "timestamp": self.min_ago,
  652. "fingerprint": ["group_1"],
  653. "user": {"email": "foo@example.com"},
  654. },
  655. project_id=project.id,
  656. )
  657. event = self.store_event(
  658. data={
  659. "event_id": "b" * 32,
  660. "timestamp": self.min_ago,
  661. "fingerprint": ["group_2"],
  662. "user": {"email": "foo@example.com"},
  663. },
  664. project_id=project.id,
  665. )
  666. self.store_event(
  667. data={
  668. "event_id": "c" * 32,
  669. "timestamp": self.min_ago,
  670. "fingerprint": ["group_2"],
  671. "user": {"email": "bar@example.com"},
  672. },
  673. project_id=project.id,
  674. )
  675. self.store_event(
  676. data={
  677. "event_id": "d" * 32,
  678. "timestamp": self.min_ago,
  679. "fingerprint": ["group_3"],
  680. "user": {"email": "bar@example.com"},
  681. },
  682. project_id=project.id,
  683. )
  684. self.store_event(
  685. data={
  686. "event_id": "e" * 32,
  687. "timestamp": self.min_ago,
  688. "fingerprint": ["group_3"],
  689. "user": {"email": "bar@example.com"},
  690. },
  691. project_id=project.id,
  692. )
  693. query = {
  694. "field": ["issue.id", "count(id)", "count_unique(user)"],
  695. "query": "count(id):>1 count_unique(user):>1",
  696. "orderby": "issue.id",
  697. }
  698. response = self.do_request(query)
  699. assert response.status_code == 200, response.content
  700. assert len(response.data["data"]) == 1
  701. data = response.data["data"]
  702. assert data[0]["issue.id"] == event.group_id
  703. assert data[0]["count_id"] == 2
  704. assert data[0]["count_unique_user"] == 2
  705. def test_aggregation_alias_comparison(self):
  706. project = self.create_project()
  707. data = load_data(
  708. "transaction",
  709. timestamp=before_now(minutes=1),
  710. start_timestamp=before_now(minutes=1, seconds=5),
  711. )
  712. data["transaction"] = "/aggregates/1"
  713. self.store_event(data, project_id=project.id)
  714. data = load_data(
  715. "transaction",
  716. timestamp=before_now(minutes=1),
  717. start_timestamp=before_now(minutes=1, seconds=3),
  718. )
  719. data["transaction"] = "/aggregates/2"
  720. event = self.store_event(data, project_id=project.id)
  721. query = {
  722. "field": ["transaction", "p95()"],
  723. "query": "event.type:transaction p95():<4000",
  724. "orderby": ["transaction"],
  725. }
  726. response = self.do_request(query)
  727. assert response.status_code == 200, response.content
  728. assert len(response.data["data"]) == 1
  729. data = response.data["data"]
  730. assert data[0]["transaction"] == event.transaction
  731. assert data[0]["p95"] == 3000
  732. def test_aggregation_comparison_with_conditions(self):
  733. project = self.create_project()
  734. self.store_event(
  735. data={
  736. "event_id": "a" * 32,
  737. "timestamp": self.min_ago,
  738. "fingerprint": ["group_1"],
  739. "user": {"email": "foo@example.com"},
  740. "environment": "prod",
  741. },
  742. project_id=project.id,
  743. )
  744. self.store_event(
  745. data={
  746. "event_id": "b" * 32,
  747. "timestamp": self.min_ago,
  748. "fingerprint": ["group_2"],
  749. "user": {"email": "foo@example.com"},
  750. "environment": "staging",
  751. },
  752. project_id=project.id,
  753. )
  754. event = self.store_event(
  755. data={
  756. "event_id": "c" * 32,
  757. "timestamp": self.min_ago,
  758. "fingerprint": ["group_2"],
  759. "user": {"email": "foo@example.com"},
  760. "environment": "prod",
  761. },
  762. project_id=project.id,
  763. )
  764. self.store_event(
  765. data={
  766. "event_id": "d" * 32,
  767. "timestamp": self.min_ago,
  768. "fingerprint": ["group_2"],
  769. "user": {"email": "foo@example.com"},
  770. "environment": "prod",
  771. },
  772. project_id=project.id,
  773. )
  774. query = {
  775. "field": ["issue.id", "count(id)"],
  776. "query": "count(id):>1 user.email:foo@example.com environment:prod",
  777. "orderby": "issue.id",
  778. }
  779. response = self.do_request(query)
  780. assert response.status_code == 200, response.content
  781. assert len(response.data["data"]) == 1
  782. data = response.data["data"]
  783. assert data[0]["issue.id"] == event.group_id
  784. assert data[0]["count_id"] == 2
  785. def test_aggregation_date_comparison_with_conditions(self):
  786. project = self.create_project()
  787. event = self.store_event(
  788. data={
  789. "event_id": "a" * 32,
  790. "timestamp": self.min_ago,
  791. "fingerprint": ["group_1"],
  792. "user": {"email": "foo@example.com"},
  793. "environment": "prod",
  794. },
  795. project_id=project.id,
  796. )
  797. self.store_event(
  798. data={
  799. "event_id": "b" * 32,
  800. "timestamp": self.min_ago,
  801. "fingerprint": ["group_2"],
  802. "user": {"email": "foo@example.com"},
  803. "environment": "staging",
  804. },
  805. project_id=project.id,
  806. )
  807. self.store_event(
  808. data={
  809. "event_id": "c" * 32,
  810. "timestamp": self.min_ago,
  811. "fingerprint": ["group_2"],
  812. "user": {"email": "foo@example.com"},
  813. "environment": "prod",
  814. },
  815. project_id=project.id,
  816. )
  817. self.store_event(
  818. data={
  819. "event_id": "d" * 32,
  820. "timestamp": self.min_ago,
  821. "fingerprint": ["group_2"],
  822. "user": {"email": "foo@example.com"},
  823. "environment": "prod",
  824. },
  825. project_id=project.id,
  826. )
  827. query = {
  828. "field": ["issue.id", "max(timestamp)"],
  829. "query": "max(timestamp):>1 user.email:foo@example.com environment:prod",
  830. "orderby": "issue.id",
  831. }
  832. response = self.do_request(query)
  833. assert response.status_code == 200, response.content
  834. assert len(response.data["data"]) == 2
  835. response.data["meta"]["max_timestamp"] == "date"
  836. data = response.data["data"]
  837. assert data[0]["issue.id"] == event.group_id
  838. def test_percentile_function(self):
  839. project = self.create_project()
  840. data = load_data(
  841. "transaction",
  842. timestamp=before_now(minutes=1),
  843. start_timestamp=before_now(minutes=1, seconds=5),
  844. )
  845. data["transaction"] = "/aggregates/1"
  846. event1 = self.store_event(data, project_id=project.id)
  847. data = load_data(
  848. "transaction",
  849. timestamp=before_now(minutes=1),
  850. start_timestamp=before_now(minutes=1, seconds=3),
  851. )
  852. data["transaction"] = "/aggregates/2"
  853. event2 = self.store_event(data, project_id=project.id)
  854. query = {
  855. "field": ["transaction", "percentile(transaction.duration, 0.95)"],
  856. "query": "event.type:transaction",
  857. "orderby": ["transaction"],
  858. }
  859. response = self.do_request(query)
  860. assert response.status_code == 200, response.content
  861. assert len(response.data["data"]) == 2
  862. data = response.data["data"]
  863. assert data[0]["transaction"] == event1.transaction
  864. assert data[0]["percentile_transaction_duration_0_95"] == 5000
  865. assert data[1]["transaction"] == event2.transaction
  866. assert data[1]["percentile_transaction_duration_0_95"] == 3000
  867. def test_percentile_function_as_condition(self):
  868. project = self.create_project()
  869. data = load_data(
  870. "transaction",
  871. timestamp=before_now(minutes=1),
  872. start_timestamp=before_now(minutes=1, seconds=5),
  873. )
  874. data["transaction"] = "/aggregates/1"
  875. event1 = self.store_event(data, project_id=project.id)
  876. data = load_data(
  877. "transaction",
  878. timestamp=before_now(minutes=1),
  879. start_timestamp=before_now(minutes=1, seconds=3),
  880. )
  881. data["transaction"] = "/aggregates/2"
  882. self.store_event(data, project_id=project.id)
  883. query = {
  884. "field": ["transaction", "percentile(transaction.duration, 0.95)"],
  885. "query": "event.type:transaction percentile(transaction.duration, 0.95):>4000",
  886. "orderby": ["transaction"],
  887. }
  888. response = self.do_request(query)
  889. assert response.status_code == 200, response.content
  890. assert len(response.data["data"]) == 1
  891. data = response.data["data"]
  892. assert data[0]["transaction"] == event1.transaction
  893. assert data[0]["percentile_transaction_duration_0_95"] == 5000
  894. def test_epm_function(self):
  895. project = self.create_project()
  896. data = load_data(
  897. "transaction",
  898. timestamp=before_now(minutes=1),
  899. start_timestamp=before_now(minutes=1, seconds=5),
  900. )
  901. data["transaction"] = "/aggregates/1"
  902. event1 = self.store_event(data, project_id=project.id)
  903. data = load_data(
  904. "transaction",
  905. timestamp=before_now(minutes=1),
  906. start_timestamp=before_now(minutes=1, seconds=3),
  907. )
  908. data["transaction"] = "/aggregates/2"
  909. event2 = self.store_event(data, project_id=project.id)
  910. query = {
  911. "field": ["transaction", "epm()"],
  912. "query": "event.type:transaction",
  913. "orderby": ["transaction"],
  914. "statsPeriod": "2m",
  915. }
  916. response = self.do_request(query)
  917. assert response.status_code == 200, response.content
  918. assert len(response.data["data"]) == 2
  919. data = response.data["data"]
  920. assert data[0]["transaction"] == event1.transaction
  921. assert data[0]["epm"] == 0.5
  922. assert data[1]["transaction"] == event2.transaction
  923. assert data[1]["epm"] == 0.5
  924. def test_nonexistent_fields(self):
  925. project = self.create_project()
  926. self.store_event(
  927. data={"event_id": "a" * 32, "message": "how to make fast", "timestamp": self.min_ago},
  928. project_id=project.id,
  929. )
  930. query = {"field": ["issue_world.id"]}
  931. response = self.do_request(query)
  932. assert response.status_code == 200, response.content
  933. assert response.data["data"][0]["issue_world.id"] == ""
  934. def test_no_requested_fields_or_grouping(self):
  935. project = self.create_project()
  936. self.store_event(
  937. data={"event_id": "a" * 32, "message": "how to make fast", "timestamp": self.min_ago},
  938. project_id=project.id,
  939. )
  940. query = {"query": "test"}
  941. response = self.do_request(query)
  942. assert response.status_code == 400, response.content
  943. assert response.data["detail"] == "No columns selected"
  944. def test_condition_on_aggregate_misses(self):
  945. project = self.create_project()
  946. self.store_event(
  947. data={
  948. "event_id": "c" * 32,
  949. "timestamp": self.min_ago,
  950. "fingerprint": ["group_2"],
  951. "user": {"email": "bar@example.com"},
  952. },
  953. project_id=project.id,
  954. )
  955. query = {"field": ["issue.id"], "query": "event_count:>0", "orderby": "issue.id"}
  956. response = self.do_request(query)
  957. assert response.status_code == 200, response.content
  958. assert len(response.data["data"]) == 0
  959. def test_next_prev_link_headers(self):
  960. project = self.create_project()
  961. events = [("a", "group_1"), ("b", "group_2"), ("c", "group_2"), ("d", "group_2")]
  962. for e in events:
  963. self.store_event(
  964. data={
  965. "event_id": e[0] * 32,
  966. "timestamp": self.min_ago,
  967. "fingerprint": [e[1]],
  968. "user": {"email": "foo@example.com"},
  969. "tags": {"language": "C++"},
  970. },
  971. project_id=project.id,
  972. )
  973. query = {
  974. "field": ["count(id)", "issue.id", "context.key"],
  975. "sort": "-count_id",
  976. "query": "language:C++",
  977. }
  978. response = self.do_request(query)
  979. assert response.status_code == 200, response.content
  980. links = parse_link_header(response["Link"])
  981. for link in links:
  982. assert "field=issue.id" in link
  983. assert "field=count%28id%29" in link
  984. assert "field=context.key" in link
  985. assert "sort=-count_id" in link
  986. assert "query=language%3AC%2B%2B" in link
  987. assert len(response.data["data"]) == 2
  988. data = response.data["data"]
  989. assert data[0]["count_id"] == 3
  990. assert data[1]["count_id"] == 1
  991. def test_empty_count_query(self):
  992. project = self.create_project()
  993. event = self.store_event(
  994. data={
  995. "event_id": "a" * 32,
  996. "timestamp": iso_format(before_now(minutes=5)),
  997. "fingerprint": ["1123581321"],
  998. "user": {"email": "foo@example.com"},
  999. "tags": {"language": "C++"},
  1000. },
  1001. project_id=project.id,
  1002. )
  1003. query = {
  1004. "field": ["count()"],
  1005. "query": "issue.id:%d timestamp:>%s" % (event.group_id, self.min_ago),
  1006. "statsPeriod": "14d",
  1007. }
  1008. response = self.do_request(query)
  1009. assert response.status_code == 200, response.content
  1010. data = response.data["data"]
  1011. assert len(data) == 1
  1012. assert data[0]["count"] == 0
  1013. def test_reference_event(self):
  1014. project = self.create_project()
  1015. reference = self.store_event(
  1016. data={
  1017. "event_id": "a" * 32,
  1018. "transaction": "/example",
  1019. "message": "how to make fast",
  1020. "timestamp": self.two_min_ago,
  1021. },
  1022. project_id=project.id,
  1023. )
  1024. self.store_event(
  1025. data={
  1026. "event_id": "b" * 32,
  1027. "transaction": "/example",
  1028. "message": "how to make more faster?",
  1029. "timestamp": self.min_ago,
  1030. },
  1031. project_id=project.id,
  1032. )
  1033. self.store_event(
  1034. data={
  1035. "event_id": "c" * 32,
  1036. "transaction": "/nomatch",
  1037. "message": "how to make fast",
  1038. "timestamp": self.min_ago,
  1039. },
  1040. project_id=project.id,
  1041. )
  1042. query = {
  1043. "field": ["transaction", "count()"],
  1044. "query": "",
  1045. "referenceEvent": "{}:{}".format(project.slug, reference.event_id),
  1046. }
  1047. response = self.do_request(query)
  1048. assert response.status_code == 200, response.content
  1049. assert len(response.data["data"]) == 1
  1050. data = response.data["data"]
  1051. assert data[0]["transaction"] == "/example"
  1052. def test_stack_wildcard_condition(self):
  1053. project = self.create_project()
  1054. data = load_data("javascript")
  1055. data["timestamp"] = self.min_ago
  1056. self.store_event(data=data, project_id=project.id)
  1057. query = {"field": ["stack.filename", "message"], "query": "stack.filename:*.js"}
  1058. response = self.do_request(query)
  1059. assert response.status_code == 200, response.content
  1060. assert len(response.data["data"]) == 1
  1061. assert response.data["meta"]["message"] == "string"
  1062. def test_email_wildcard_condition(self):
  1063. project = self.create_project()
  1064. data = load_data("javascript")
  1065. data["timestamp"] = self.min_ago
  1066. self.store_event(data=data, project_id=project.id)
  1067. query = {"field": ["stack.filename", "message"], "query": "user.email:*@example.org"}
  1068. response = self.do_request(query)
  1069. assert response.status_code == 200, response.content
  1070. assert len(response.data["data"]) == 1
  1071. assert response.data["meta"]["message"] == "string"
  1072. def test_transaction_event_type(self):
  1073. project = self.create_project()
  1074. data = load_data(
  1075. "transaction",
  1076. timestamp=before_now(minutes=1),
  1077. start_timestamp=before_now(minutes=1, seconds=5),
  1078. )
  1079. self.store_event(data=data, project_id=project.id)
  1080. query = {
  1081. "field": ["transaction", "transaction.duration", "transaction.status"],
  1082. "query": "event.type:transaction",
  1083. }
  1084. response = self.do_request(query)
  1085. assert response.status_code == 200, response.content
  1086. assert len(response.data["data"]) == 1
  1087. assert response.data["meta"]["transaction.duration"] == "duration"
  1088. assert response.data["meta"]["transaction.status"] == "string"
  1089. assert response.data["data"][0]["transaction.status"] == "ok"
  1090. def test_trace_columns(self):
  1091. project = self.create_project()
  1092. data = load_data(
  1093. "transaction",
  1094. timestamp=before_now(minutes=1),
  1095. start_timestamp=before_now(minutes=1, seconds=5),
  1096. )
  1097. self.store_event(data=data, project_id=project.id)
  1098. query = {"field": ["trace"], "query": "event.type:transaction"}
  1099. response = self.do_request(query)
  1100. assert response.status_code == 200, response.content
  1101. assert len(response.data["data"]) == 1
  1102. assert response.data["meta"]["trace"] == "string"
  1103. assert response.data["data"][0]["trace"] == data["contexts"]["trace"]["trace_id"]
  1104. def test_issue_in_columns(self):
  1105. project1 = self.create_project()
  1106. project2 = self.create_project()
  1107. event1 = self.store_event(
  1108. data={
  1109. "event_id": "a" * 32,
  1110. "transaction": "/example",
  1111. "message": "how to make fast",
  1112. "timestamp": self.two_min_ago,
  1113. "fingerprint": ["group_1"],
  1114. },
  1115. project_id=project1.id,
  1116. )
  1117. event2 = self.store_event(
  1118. data={
  1119. "event_id": "b" * 32,
  1120. "transaction": "/example",
  1121. "message": "how to make fast",
  1122. "timestamp": self.two_min_ago,
  1123. "fingerprint": ["group_1"],
  1124. },
  1125. project_id=project2.id,
  1126. )
  1127. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1128. query = {"field": ["id", "issue"], "orderby": ["id"]}
  1129. response = self.do_request(query, features=features)
  1130. assert response.status_code == 200, response.content
  1131. data = response.data["data"]
  1132. assert len(data) == 2
  1133. assert data[0]["id"] == event1.event_id
  1134. assert data[0]["issue.id"] == event1.group_id
  1135. assert data[0]["issue"] == event1.group.qualified_short_id
  1136. assert data[1]["id"] == event2.event_id
  1137. assert data[1]["issue.id"] == event2.group_id
  1138. assert data[1]["issue"] == event2.group.qualified_short_id
  1139. def test_issue_in_search_and_columns(self):
  1140. project1 = self.create_project()
  1141. project2 = self.create_project()
  1142. event1 = self.store_event(
  1143. data={
  1144. "event_id": "a" * 32,
  1145. "transaction": "/example",
  1146. "message": "how to make fast",
  1147. "timestamp": self.two_min_ago,
  1148. "fingerprint": ["group_1"],
  1149. },
  1150. project_id=project1.id,
  1151. )
  1152. self.store_event(
  1153. data={
  1154. "event_id": "b" * 32,
  1155. "transaction": "/example",
  1156. "message": "how to make fast",
  1157. "timestamp": self.two_min_ago,
  1158. "fingerprint": ["group_1"],
  1159. },
  1160. project_id=project2.id,
  1161. )
  1162. tests = [
  1163. ("issue", "issue:%s" % event1.group.qualified_short_id),
  1164. ("issue.id", "issue:%s" % event1.group.qualified_short_id),
  1165. ("issue", "issue.id:%s" % event1.group_id),
  1166. ("issue.id", "issue.id:%s" % event1.group_id),
  1167. ]
  1168. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1169. for testdata in tests:
  1170. query = {"field": [testdata[0]], "query": testdata[1]}
  1171. response = self.do_request(query, features=features)
  1172. assert response.status_code == 200, response.content
  1173. data = response.data["data"]
  1174. assert len(data) == 1
  1175. assert data[0]["id"] == event1.event_id
  1176. assert data[0]["issue.id"] == event1.group_id
  1177. if testdata[0] == "issue":
  1178. assert data[0]["issue"] == event1.group.qualified_short_id
  1179. else:
  1180. assert data[0].get("issue", None) is None
  1181. def test_issue_negation(self):
  1182. project1 = self.create_project()
  1183. project2 = self.create_project()
  1184. event1 = self.store_event(
  1185. data={
  1186. "event_id": "a" * 32,
  1187. "transaction": "/example",
  1188. "message": "how to make fast",
  1189. "timestamp": self.two_min_ago,
  1190. "fingerprint": ["group_1"],
  1191. },
  1192. project_id=project1.id,
  1193. )
  1194. event2 = self.store_event(
  1195. data={
  1196. "event_id": "b" * 32,
  1197. "transaction": "/example",
  1198. "message": "go really fast plz",
  1199. "timestamp": self.two_min_ago,
  1200. "fingerprint": ["group_2"],
  1201. },
  1202. project_id=project2.id,
  1203. )
  1204. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1205. query = {
  1206. "field": ["title", "issue.id"],
  1207. "query": "!issue:{}".format(event1.group.qualified_short_id),
  1208. }
  1209. response = self.do_request(query, features=features)
  1210. assert response.status_code == 200, response.content
  1211. data = response.data["data"]
  1212. assert len(data) == 1
  1213. assert data[0]["title"] == event2.title
  1214. assert data[0]["issue.id"] == event2.group_id
  1215. def test_search_for_nonexistent_issue(self):
  1216. project1 = self.create_project()
  1217. self.store_event(
  1218. data={
  1219. "event_id": "a" * 32,
  1220. "transaction": "/example",
  1221. "message": "how to make fast",
  1222. "timestamp": self.two_min_ago,
  1223. "fingerprint": ["group_1"],
  1224. },
  1225. project_id=project1.id,
  1226. )
  1227. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1228. query = {"field": ["count()"], "query": "issue.id:112358"}
  1229. response = self.do_request(query, features=features)
  1230. assert response.status_code == 200, response.content
  1231. data = response.data["data"]
  1232. assert len(data) == 1
  1233. assert data[0]["count"] == 0
  1234. def test_issue_alias_inside_aggregate(self):
  1235. project1 = self.create_project()
  1236. self.store_event(
  1237. data={
  1238. "event_id": "a" * 32,
  1239. "transaction": "/example",
  1240. "message": "how to make fast",
  1241. "timestamp": self.two_min_ago,
  1242. "fingerprint": ["group_1"],
  1243. },
  1244. project_id=project1.id,
  1245. )
  1246. self.store_event(
  1247. data={
  1248. "event_id": "b" * 32,
  1249. "transaction": "/example",
  1250. "message": "how to make fast",
  1251. "timestamp": self.two_min_ago,
  1252. "fingerprint": ["group_2"],
  1253. },
  1254. project_id=project1.id,
  1255. )
  1256. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1257. query = {
  1258. "field": ["project", "count(id)", "count_unique(issue.id)", "count_unique(issue)"],
  1259. "sort": "-count(id)",
  1260. "statsPeriod": "24h",
  1261. }
  1262. response = self.do_request(query, features=features)
  1263. assert response.status_code == 200, response.content
  1264. data = response.data["data"]
  1265. assert len(data) == 1
  1266. assert data[0]["count_id"] == 2
  1267. assert data[0]["count_unique_issue_id"] == 2
  1268. assert data[0]["count_unique_issue"] == 2
  1269. def test_project_alias_inside_aggregate(self):
  1270. project1 = self.create_project()
  1271. project2 = self.create_project()
  1272. self.store_event(
  1273. data={
  1274. "event_id": "a" * 32,
  1275. "transaction": "/example",
  1276. "message": "how to make fast",
  1277. "timestamp": self.two_min_ago,
  1278. "fingerprint": ["group_1"],
  1279. },
  1280. project_id=project1.id,
  1281. )
  1282. self.store_event(
  1283. data={
  1284. "event_id": "b" * 32,
  1285. "transaction": "/example",
  1286. "message": "how to make fast",
  1287. "timestamp": self.two_min_ago,
  1288. "fingerprint": ["group_2"],
  1289. },
  1290. project_id=project2.id,
  1291. )
  1292. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1293. query = {
  1294. "field": [
  1295. "event.type",
  1296. "count(id)",
  1297. "count_unique(project.id)",
  1298. "count_unique(project)",
  1299. ],
  1300. "sort": "-count(id)",
  1301. "statsPeriod": "24h",
  1302. }
  1303. response = self.do_request(query, features=features)
  1304. assert response.status_code == 200, response.content
  1305. data = response.data["data"]
  1306. assert len(data) == 1
  1307. assert data[0]["count_id"] == 2
  1308. assert data[0]["count_unique_project_id"] == 2
  1309. assert data[0]["count_unique_project"] == 2
  1310. def test_user_display(self):
  1311. project1 = self.create_project()
  1312. project2 = self.create_project()
  1313. self.store_event(
  1314. data={
  1315. "event_id": "a" * 32,
  1316. "transaction": "/example",
  1317. "message": "how to make fast",
  1318. "timestamp": self.two_min_ago,
  1319. "user": {"email": "cathy@example.com"},
  1320. },
  1321. project_id=project1.id,
  1322. )
  1323. self.store_event(
  1324. data={
  1325. "event_id": "b" * 32,
  1326. "transaction": "/example",
  1327. "message": "how to make fast",
  1328. "timestamp": self.two_min_ago,
  1329. "user": {"username": "catherine"},
  1330. },
  1331. project_id=project2.id,
  1332. )
  1333. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1334. query = {
  1335. "field": ["event.type", "user.display"],
  1336. "query": "user.display:cath*",
  1337. "statsPeriod": "24h",
  1338. }
  1339. response = self.do_request(query, features=features)
  1340. assert response.status_code == 200, response.content
  1341. data = response.data["data"]
  1342. assert len(data) == 2
  1343. result = set([r["user.display"] for r in data])
  1344. assert result == set(["catherine", "cathy@example.com"])
  1345. def test_user_display_with_aggregates(self):
  1346. self.login_as(user=self.user)
  1347. project1 = self.create_project()
  1348. self.store_event(
  1349. data={
  1350. "event_id": "a" * 32,
  1351. "transaction": "/example",
  1352. "message": "how to make fast",
  1353. "timestamp": self.two_min_ago,
  1354. "user": {"email": "cathy@example.com"},
  1355. },
  1356. project_id=project1.id,
  1357. )
  1358. with self.feature(
  1359. {"organizations:discover-basic": True, "organizations:global-views": True}
  1360. ):
  1361. response = self.client.get(
  1362. self.url,
  1363. format="json",
  1364. data={
  1365. "field": ["event.type", "user.display", "count_unique(title)"],
  1366. "statsPeriod": "24h",
  1367. },
  1368. )
  1369. assert response.status_code == 200, response.content
  1370. data = response.data["data"]
  1371. assert len(data) == 1
  1372. result = set([r["user.display"] for r in data])
  1373. assert result == set(["cathy@example.com"])
  1374. with self.feature(
  1375. {"organizations:discover-basic": True, "organizations:global-views": True}
  1376. ):
  1377. response = self.client.get(
  1378. self.url,
  1379. format="json",
  1380. data={"field": ["event.type", "count_unique(user.display)"], "statsPeriod": "24h"},
  1381. )
  1382. assert response.status_code == 200, response.content
  1383. data = response.data["data"]
  1384. assert len(data) == 1
  1385. assert data[0]["count_unique_user_display"] == 1
  1386. def test_has_transaction_status(self):
  1387. project = self.create_project()
  1388. data = load_data("transaction", timestamp=before_now(minutes=1))
  1389. data["transaction"] = "/transactionstatus/1"
  1390. self.store_event(data, project_id=project.id)
  1391. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1392. query = {
  1393. "field": ["event.type", "count(id)"],
  1394. "query": "event.type:transaction has:transaction.status",
  1395. "sort": "-count(id)",
  1396. "statsPeriod": "24h",
  1397. }
  1398. response = self.do_request(query, features=features)
  1399. assert response.status_code == 200, response.content
  1400. data = response.data["data"]
  1401. assert len(data) == 1
  1402. assert data[0]["count_id"] == 1
  1403. def test_not_has_transaction_status(self):
  1404. project = self.create_project()
  1405. data = load_data("transaction", timestamp=before_now(minutes=1))
  1406. data["transaction"] = "/transactionstatus/1"
  1407. self.store_event(data, project_id=project.id)
  1408. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1409. query = {
  1410. "field": ["event.type", "count(id)"],
  1411. "query": "event.type:transaction !has:transaction.status",
  1412. "sort": "-count(id)",
  1413. "statsPeriod": "24h",
  1414. }
  1415. response = self.do_request(query, features=features)
  1416. assert response.status_code == 200, response.content
  1417. data = response.data["data"]
  1418. assert len(data) == 1
  1419. assert data[0]["count_id"] == 0
  1420. def test_tag_that_looks_like_aggregation(self):
  1421. project = self.create_project()
  1422. data = {
  1423. "message": "Failure state",
  1424. "timestamp": self.two_min_ago,
  1425. "tags": {"count_diff": 99},
  1426. }
  1427. self.store_event(data, project_id=project.id)
  1428. query = {
  1429. "field": ["message", "count_diff", "count()"],
  1430. "query": "",
  1431. "project": [project.id],
  1432. "statsPeriod": "24h",
  1433. }
  1434. response = self.do_request(query)
  1435. assert response.status_code == 200, response.content
  1436. meta = response.data["meta"]
  1437. assert "string" == meta["count_diff"], "tags should not be counted as integers"
  1438. assert "string" == meta["message"]
  1439. assert "integer" == meta["count"]
  1440. assert 1 == len(response.data["data"])
  1441. data = response.data["data"][0]
  1442. assert "99" == data["count_diff"]
  1443. assert "Failure state" == data["message"]
  1444. assert 1 == data["count"]
  1445. def test_aggregate_negation(self):
  1446. project = self.create_project()
  1447. data = load_data(
  1448. "transaction",
  1449. timestamp=before_now(minutes=1),
  1450. start_timestamp=before_now(minutes=1, seconds=5),
  1451. )
  1452. self.store_event(data, project_id=project.id)
  1453. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1454. query = {
  1455. "field": ["event.type", "p99()"],
  1456. "query": "event.type:transaction p99():5s",
  1457. "statsPeriod": "24h",
  1458. }
  1459. response = self.do_request(query, features=features)
  1460. assert response.status_code == 200, response.content
  1461. data = response.data["data"]
  1462. assert len(data) == 1
  1463. query = {
  1464. "field": ["event.type", "p99()"],
  1465. "query": "event.type:transaction !p99():5s",
  1466. "statsPeriod": "24h",
  1467. }
  1468. response = self.do_request(query, features=features)
  1469. assert response.status_code == 200, response.content
  1470. data = response.data["data"]
  1471. assert len(data) == 0
  1472. def test_all_aggregates_in_columns(self):
  1473. project = self.create_project()
  1474. data = load_data(
  1475. "transaction",
  1476. timestamp=before_now(minutes=2),
  1477. start_timestamp=before_now(minutes=2, seconds=5),
  1478. )
  1479. data["transaction"] = "/failure_rate/1"
  1480. self.store_event(data, project_id=project.id)
  1481. data = load_data(
  1482. "transaction",
  1483. timestamp=before_now(minutes=1),
  1484. start_timestamp=before_now(minutes=1, seconds=5),
  1485. )
  1486. data["transaction"] = "/failure_rate/1"
  1487. data["contexts"]["trace"]["status"] = "unauthenticated"
  1488. event = self.store_event(data, project_id=project.id)
  1489. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1490. query = {
  1491. "field": [
  1492. "event.type",
  1493. "p50()",
  1494. "p75()",
  1495. "p95()",
  1496. "p99()",
  1497. "p100()",
  1498. "percentile(transaction.duration, 0.99)",
  1499. "apdex(300)",
  1500. "user_misery(300)",
  1501. "failure_rate()",
  1502. ],
  1503. "query": "event.type:transaction",
  1504. }
  1505. response = self.do_request(query, features=features)
  1506. assert response.status_code == 200, response.content
  1507. meta = response.data["meta"]
  1508. assert meta["p50"] == "duration"
  1509. assert meta["p75"] == "duration"
  1510. assert meta["p95"] == "duration"
  1511. assert meta["p99"] == "duration"
  1512. assert meta["p100"] == "duration"
  1513. assert meta["percentile_transaction_duration_0_99"] == "duration"
  1514. assert meta["apdex_300"] == "number"
  1515. assert meta["failure_rate"] == "percentage"
  1516. assert meta["user_misery_300"] == "number"
  1517. data = response.data["data"]
  1518. assert len(data) == 1
  1519. assert data[0]["p50"] == 5000
  1520. assert data[0]["p75"] == 5000
  1521. assert data[0]["p95"] == 5000
  1522. assert data[0]["p99"] == 5000
  1523. assert data[0]["p100"] == 5000
  1524. assert data[0]["percentile_transaction_duration_0_99"] == 5000
  1525. assert data[0]["apdex_300"] == 0.0
  1526. assert data[0]["user_misery_300"] == 1
  1527. assert data[0]["failure_rate"] == 0.5
  1528. query = {
  1529. "field": ["event.type", "last_seen()", "latest_event()"],
  1530. "query": "event.type:transaction",
  1531. }
  1532. response = self.do_request(query, features=features)
  1533. assert response.status_code == 200, response.content
  1534. data = response.data["data"]
  1535. assert len(data) == 1
  1536. assert iso_format(before_now(minutes=1))[:-5] in data[0]["last_seen"]
  1537. assert data[0]["latest_event"] == event.event_id
  1538. query = {
  1539. "field": [
  1540. "event.type",
  1541. "count()",
  1542. "count(id)",
  1543. "count_unique(project)",
  1544. "min(transaction.duration)",
  1545. "max(transaction.duration)",
  1546. "avg(transaction.duration)",
  1547. "sum(transaction.duration)",
  1548. ],
  1549. "query": "event.type:transaction",
  1550. }
  1551. response = self.do_request(query, features=features)
  1552. assert response.status_code == 200, response.content
  1553. data = response.data["data"]
  1554. assert len(data) == 1
  1555. assert data[0]["count"] == 2
  1556. assert data[0]["count_id"] == 2
  1557. assert data[0]["count_unique_project"] == 1
  1558. assert data[0]["min_transaction_duration"] == 5000
  1559. assert data[0]["max_transaction_duration"] == 5000
  1560. assert data[0]["avg_transaction_duration"] == 5000
  1561. assert data[0]["sum_transaction_duration"] == 10000
  1562. def test_all_aggregates_in_query(self):
  1563. project = self.create_project()
  1564. data = load_data(
  1565. "transaction",
  1566. timestamp=before_now(minutes=2),
  1567. start_timestamp=before_now(minutes=2, seconds=5),
  1568. )
  1569. data["transaction"] = "/failure_rate/1"
  1570. self.store_event(data, project_id=project.id)
  1571. data = load_data(
  1572. "transaction",
  1573. timestamp=before_now(minutes=1),
  1574. start_timestamp=before_now(minutes=1, seconds=5),
  1575. )
  1576. data["transaction"] = "/failure_rate/2"
  1577. data["contexts"]["trace"]["status"] = "unauthenticated"
  1578. self.store_event(data, project_id=project.id)
  1579. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1580. query = {
  1581. "field": [
  1582. "event.type",
  1583. "p50()",
  1584. "p75()",
  1585. "p95()",
  1586. "percentile(transaction.duration, 0.99)",
  1587. "p100()",
  1588. ],
  1589. "query": "event.type:transaction p50():>100 p75():>1000 p95():>1000 p100():>1000 percentile(transaction.duration, 0.99):>1000",
  1590. }
  1591. response = self.do_request(query, features=features)
  1592. assert response.status_code == 200, response.content
  1593. data = response.data["data"]
  1594. assert len(data) == 1
  1595. assert data[0]["p50"] == 5000
  1596. assert data[0]["p75"] == 5000
  1597. assert data[0]["p95"] == 5000
  1598. assert data[0]["p100"] == 5000
  1599. assert data[0]["percentile_transaction_duration_0_99"] == 5000
  1600. query = {
  1601. "field": ["event.type", "apdex(300)", "user_misery(300)", "failure_rate()"],
  1602. "query": "event.type:transaction apdex(300):>-1.0 failure_rate():>0.25",
  1603. }
  1604. response = self.do_request(query, features=features)
  1605. assert response.status_code == 200, response.content
  1606. data = response.data["data"]
  1607. assert len(data) == 1
  1608. assert data[0]["apdex_300"] == 0.0
  1609. assert data[0]["user_misery_300"] == 1
  1610. assert data[0]["failure_rate"] == 0.5
  1611. query = {
  1612. "field": ["event.type", "last_seen()", "latest_event()"],
  1613. "query": u"event.type:transaction last_seen():>1990-12-01T00:00:00",
  1614. }
  1615. response = self.do_request(query, features=features)
  1616. assert response.status_code == 200, response.content
  1617. data = response.data["data"]
  1618. assert len(data) == 1
  1619. query = {
  1620. "field": ["event.type", "count()", "count(id)", "count_unique(transaction)"],
  1621. "query": "event.type:transaction count():>1 count(id):>1 count_unique(transaction):>1",
  1622. }
  1623. response = self.do_request(query, features=features)
  1624. assert response.status_code == 200, response.content
  1625. data = response.data["data"]
  1626. assert len(data) == 1
  1627. assert data[0]["count"] == 2
  1628. assert data[0]["count_id"] == 2
  1629. assert data[0]["count_unique_transaction"] == 2
  1630. query = {
  1631. "field": [
  1632. "event.type",
  1633. "min(transaction.duration)",
  1634. "max(transaction.duration)",
  1635. "avg(transaction.duration)",
  1636. "sum(transaction.duration)",
  1637. ],
  1638. "query": "event.type:transaction min(transaction.duration):>1000 max(transaction.duration):>1000 avg(transaction.duration):>1000 sum(transaction.duration):>1000",
  1639. }
  1640. response = self.do_request(query, features=features)
  1641. assert response.status_code == 200, response.content
  1642. data = response.data["data"]
  1643. assert len(data) == 1
  1644. assert data[0]["min_transaction_duration"] == 5000
  1645. assert data[0]["max_transaction_duration"] == 5000
  1646. assert data[0]["avg_transaction_duration"] == 5000
  1647. assert data[0]["sum_transaction_duration"] == 10000
  1648. query = {
  1649. "field": ["event.type", "apdex(400)"],
  1650. "query": "event.type:transaction apdex(400):0",
  1651. }
  1652. response = self.do_request(query, features=features)
  1653. assert response.status_code == 200, response.content
  1654. data = response.data["data"]
  1655. assert len(data) == 1
  1656. assert data[0]["apdex_400"] == 0
  1657. def test_functions_in_orderby(self):
  1658. project = self.create_project()
  1659. data = load_data(
  1660. "transaction",
  1661. timestamp=before_now(minutes=2),
  1662. start_timestamp=before_now(minutes=2, seconds=5),
  1663. )
  1664. data["transaction"] = "/failure_rate/1"
  1665. self.store_event(data, project_id=project.id)
  1666. data = load_data(
  1667. "transaction",
  1668. timestamp=before_now(minutes=1),
  1669. start_timestamp=before_now(minutes=1, seconds=5),
  1670. )
  1671. data["transaction"] = "/failure_rate/2"
  1672. data["contexts"]["trace"]["status"] = "unauthenticated"
  1673. event = self.store_event(data, project_id=project.id)
  1674. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1675. query = {
  1676. "field": ["event.type", "p75()"],
  1677. "sort": "-p75",
  1678. "query": "event.type:transaction",
  1679. }
  1680. response = self.do_request(query, features=features)
  1681. assert response.status_code == 200, response.content
  1682. data = response.data["data"]
  1683. assert len(data) == 1
  1684. assert data[0]["p75"] == 5000
  1685. query = {
  1686. "field": ["event.type", "percentile(transaction.duration, 0.99)"],
  1687. "sort": "-percentile_transaction_duration_0_99",
  1688. "query": "event.type:transaction",
  1689. }
  1690. response = self.do_request(query, features=features)
  1691. assert response.status_code == 200, response.content
  1692. data = response.data["data"]
  1693. assert len(data) == 1
  1694. assert data[0]["percentile_transaction_duration_0_99"] == 5000
  1695. query = {
  1696. "field": ["event.type", "apdex(300)"],
  1697. "sort": "-apdex(300)",
  1698. "query": "event.type:transaction",
  1699. }
  1700. response = self.do_request(query, features=features)
  1701. assert response.status_code == 200, response.content
  1702. data = response.data["data"]
  1703. assert len(data) == 1
  1704. assert data[0]["apdex_300"] == 0.0
  1705. query = {
  1706. "field": ["event.type", "latest_event()"],
  1707. "query": u"event.type:transaction",
  1708. "sort": "latest_event",
  1709. }
  1710. response = self.do_request(query, features=features)
  1711. assert response.status_code == 200, response.content
  1712. data = response.data["data"]
  1713. assert len(data) == 1
  1714. assert data[0]["latest_event"] == event.event_id
  1715. query = {
  1716. "field": ["event.type", "count_unique(transaction)"],
  1717. "query": "event.type:transaction",
  1718. "sort": "-count_unique_transaction",
  1719. }
  1720. response = self.do_request(query, features=features)
  1721. assert response.status_code == 200, response.content
  1722. data = response.data["data"]
  1723. assert len(data) == 1
  1724. assert data[0]["count_unique_transaction"] == 2
  1725. query = {
  1726. "field": ["event.type", "min(transaction.duration)"],
  1727. "query": "event.type:transaction",
  1728. "sort": "-min_transaction_duration",
  1729. }
  1730. response = self.do_request(query, features=features)
  1731. assert response.status_code == 200, response.content
  1732. data = response.data["data"]
  1733. assert len(data) == 1
  1734. assert data[0]["min_transaction_duration"] == 5000
  1735. def test_issue_alias_in_aggregate(self):
  1736. project = self.create_project()
  1737. self.store_event(
  1738. data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
  1739. project_id=project.id,
  1740. )
  1741. self.store_event(
  1742. data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group_2"]},
  1743. project_id=project.id,
  1744. )
  1745. query = {
  1746. "field": ["event.type", "count_unique(issue)"],
  1747. "query": "count_unique(issue):>1",
  1748. }
  1749. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1750. response = self.do_request(query, features=features)
  1751. assert response.status_code == 200, response.content
  1752. data = response.data["data"]
  1753. assert len(data) == 1
  1754. assert data[0]["count_unique_issue"] == 2
  1755. def test_deleted_issue_in_results(self):
  1756. project = self.create_project()
  1757. event1 = self.store_event(
  1758. data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
  1759. project_id=project.id,
  1760. )
  1761. event2 = self.store_event(
  1762. data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group_2"]},
  1763. project_id=project.id,
  1764. )
  1765. event2.group.delete()
  1766. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1767. query = {"field": ["issue", "count()"], "sort": "issue"}
  1768. response = self.do_request(query, features=features)
  1769. assert response.status_code == 200, response.content
  1770. data = response.data["data"]
  1771. assert len(data) == 2
  1772. assert data[0]["issue"] == event1.group.qualified_short_id
  1773. assert data[1]["issue"] == "unknown"
  1774. def test_last_seen_negative_duration(self):
  1775. project = self.create_project()
  1776. self.store_event(
  1777. data={"event_id": "f" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
  1778. project_id=project.id,
  1779. )
  1780. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1781. query = {"field": ["id", "last_seen()"], "query": "last_seen():-30d"}
  1782. response = self.do_request(query, features=features)
  1783. assert response.status_code == 200, response.content
  1784. data = response.data["data"]
  1785. assert len(data) == 1
  1786. assert data[0]["id"] == "f" * 32
  1787. def test_last_seen_aggregate_condition(self):
  1788. project = self.create_project()
  1789. self.store_event(
  1790. data={"event_id": "f" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
  1791. project_id=project.id,
  1792. )
  1793. query = {
  1794. "field": ["id", "last_seen()"],
  1795. "query": "last_seen():>{}".format(iso_format(before_now(days=30))),
  1796. }
  1797. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1798. response = self.do_request(query, features=features)
  1799. assert response.status_code == 200, response.content
  1800. data = response.data["data"]
  1801. assert len(data) == 1
  1802. assert data[0]["id"] == "f" * 32
  1803. def test_conditional_filter(self):
  1804. project = self.create_project()
  1805. for v in ["a", "b"]:
  1806. self.store_event(
  1807. data={
  1808. "event_id": v * 32,
  1809. "timestamp": self.two_min_ago,
  1810. "fingerprint": ["group_1"],
  1811. },
  1812. project_id=project.id,
  1813. )
  1814. query = {
  1815. "field": ["id"],
  1816. "query": "id:{} OR id:{}".format("a" * 32, "b" * 32),
  1817. "orderby": "id",
  1818. }
  1819. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1820. response = self.do_request(query, features=features)
  1821. assert response.status_code == 200, response.content
  1822. data = response.data["data"]
  1823. assert len(data) == 2
  1824. assert data[0]["id"] == "a" * 32
  1825. assert data[1]["id"] == "b" * 32
  1826. def test_aggregation_comparison_with_conditional_filter(self):
  1827. project = self.create_project()
  1828. self.store_event(
  1829. data={
  1830. "event_id": "a" * 32,
  1831. "timestamp": self.min_ago,
  1832. "fingerprint": ["group_1"],
  1833. "user": {"email": "foo@example.com"},
  1834. "environment": "prod",
  1835. },
  1836. project_id=project.id,
  1837. )
  1838. self.store_event(
  1839. data={
  1840. "event_id": "b" * 32,
  1841. "timestamp": self.min_ago,
  1842. "fingerprint": ["group_2"],
  1843. "user": {"email": "foo@example.com"},
  1844. "environment": "staging",
  1845. },
  1846. project_id=project.id,
  1847. )
  1848. event = self.store_event(
  1849. data={
  1850. "event_id": "c" * 32,
  1851. "timestamp": self.min_ago,
  1852. "fingerprint": ["group_2"],
  1853. "user": {"email": "foo@example.com"},
  1854. "environment": "prod",
  1855. },
  1856. project_id=project.id,
  1857. )
  1858. self.store_event(
  1859. data={
  1860. "event_id": "d" * 32,
  1861. "timestamp": self.min_ago,
  1862. "fingerprint": ["group_2"],
  1863. "user": {"email": "foo@example.com"},
  1864. "environment": "canary",
  1865. },
  1866. project_id=project.id,
  1867. )
  1868. query = {
  1869. "field": ["issue.id", "count(id)"],
  1870. "query": "count(id):>1 user.email:foo@example.com AND (environment:prod OR environment:staging)",
  1871. "orderby": "issue.id",
  1872. }
  1873. response = self.do_request(query)
  1874. assert response.status_code == 200, response.content
  1875. assert len(response.data["data"]) == 1
  1876. data = response.data["data"]
  1877. assert data[0]["issue.id"] == event.group_id
  1878. assert data[0]["count_id"] == 2
  1879. def test_messed_up_function_values(self):
  1880. # TODO (evanh): It would be nice if this surfaced an error to the user.
  1881. # The problem: The && causes the parser to treat that term not as a bad
  1882. # function call but a valid raw search with parens in it. It's not trivial
  1883. # to change the parser to recognize "bad function values" and surface them.
  1884. project = self.create_project()
  1885. for v in ["a", "b"]:
  1886. self.store_event(
  1887. data={
  1888. "event_id": v * 32,
  1889. "timestamp": self.two_min_ago,
  1890. "fingerprint": ["group_1"],
  1891. },
  1892. project_id=project.id,
  1893. )
  1894. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1895. query = {
  1896. "field": [
  1897. "transaction",
  1898. "project",
  1899. "epm()",
  1900. "p50()",
  1901. "p95()",
  1902. "failure_rate()",
  1903. "apdex(300)",
  1904. "count_unique(user)",
  1905. "user_misery(300)",
  1906. ],
  1907. "query": "failure_rate():>0.003&& users:>10 event.type:transaction",
  1908. "sort": "-failure_rate",
  1909. "statsPeriod": "24h",
  1910. }
  1911. response = self.do_request(query, features=features)
  1912. assert response.status_code == 200, response.content
  1913. data = response.data["data"]
  1914. assert len(data) == 0
  1915. def test_context_fields_between_datasets(self):
  1916. project = self.create_project()
  1917. event_data = load_data("android")
  1918. transaction_data = load_data("transaction")
  1919. event_data["spans"] = transaction_data["spans"]
  1920. event_data["contexts"]["trace"] = transaction_data["contexts"]["trace"]
  1921. event_data["type"] = "transaction"
  1922. event_data["transaction"] = "/failure_rate/1"
  1923. event_data["timestamp"] = iso_format(before_now(minutes=1))
  1924. event_data["start_timestamp"] = iso_format(before_now(minutes=1, seconds=5))
  1925. event_data["user"]["geo"] = {"country_code": "US", "region": "CA", "city": "San Francisco"}
  1926. event_data["contexts"]["http"] = {
  1927. "method": "GET",
  1928. "referer": "something.something",
  1929. "url": "https://areyouasimulation.com",
  1930. }
  1931. self.store_event(event_data, project_id=project.id)
  1932. event_data["type"] = "error"
  1933. self.store_event(event_data, project_id=project.id)
  1934. fields = [
  1935. "http.method",
  1936. "http.referer",
  1937. "http.url",
  1938. "os.build",
  1939. "os.kernel_version",
  1940. "device.arch",
  1941. "device.battery_level",
  1942. "device.brand",
  1943. "device.charging",
  1944. "device.locale",
  1945. "device.model_id",
  1946. "device.name",
  1947. "device.online",
  1948. "device.orientation",
  1949. "device.simulator",
  1950. "device.uuid",
  1951. ]
  1952. data = [
  1953. {"field": fields + ["location", "count()"], "query": "event.type:error"},
  1954. {"field": fields + ["duration", "count()"], "query": "event.type:transaction"},
  1955. ]
  1956. for datum in data:
  1957. response = self.do_request(datum)
  1958. assert response.status_code == 200, response.content
  1959. assert len(response.data["data"]) == 1, datum
  1960. results = response.data["data"]
  1961. assert results[0]["count"] == 1, datum
  1962. for field in fields:
  1963. key, value = field.split(".", 1)
  1964. expected = six.text_type(event_data["contexts"][key][value])
  1965. assert results[0][field] == expected, field + six.text_type(datum)
  1966. def test_histogram_function(self):
  1967. project = self.create_project()
  1968. start = before_now(minutes=2).replace(microsecond=0)
  1969. latencies = [
  1970. (1, 500, 5),
  1971. (1000, 1500, 4),
  1972. (3000, 3500, 3),
  1973. (6000, 6500, 2),
  1974. (10000, 10000, 1), # just to make the math easy
  1975. ]
  1976. values = []
  1977. for bucket in latencies:
  1978. for i in range(bucket[2]):
  1979. # Don't generate a wide range of variance as the buckets can mis-align.
  1980. milliseconds = random.randint(bucket[0], bucket[1])
  1981. values.append(milliseconds)
  1982. data = load_data("transaction")
  1983. data["transaction"] = "/failure_rate/{}".format(milliseconds)
  1984. data["timestamp"] = iso_format(start)
  1985. data["start_timestamp"] = (start - timedelta(milliseconds=milliseconds)).isoformat()
  1986. self.store_event(data, project_id=project.id)
  1987. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  1988. query = {
  1989. "field": ["histogram(transaction.duration, 10)", "count()"],
  1990. "query": "event.type:transaction",
  1991. "sort": "histogram_transaction_duration_10",
  1992. }
  1993. response = self.do_request(query, features=features)
  1994. assert response.status_code == 200, response.content
  1995. data = response.data["data"]
  1996. assert len(data) == 11
  1997. bucket_size = ceil((max(values) - min(values)) / 10.0)
  1998. expected = [
  1999. (0, 5),
  2000. (bucket_size, 4),
  2001. (bucket_size * 2, 0),
  2002. (bucket_size * 3, 3),
  2003. (bucket_size * 4, 0),
  2004. (bucket_size * 5, 0),
  2005. (bucket_size * 6, 2),
  2006. (bucket_size * 7, 0),
  2007. (bucket_size * 8, 0),
  2008. (bucket_size * 9, 0),
  2009. (bucket_size * 10, 1),
  2010. ]
  2011. for idx, datum in enumerate(data):
  2012. assert datum["histogram_transaction_duration_10"] == expected[idx][0]
  2013. assert datum["count"] == expected[idx][1]
  2014. def test_histogram_function_with_filters(self):
  2015. project = self.create_project()
  2016. start = before_now(minutes=2).replace(microsecond=0)
  2017. latencies = [
  2018. (1, 500, 5),
  2019. (1000, 1500, 4),
  2020. (3000, 3500, 3),
  2021. (6000, 6500, 2),
  2022. (10000, 10000, 1), # just to make the math easy
  2023. ]
  2024. values = []
  2025. for bucket in latencies:
  2026. for i in range(bucket[2]):
  2027. milliseconds = random.randint(bucket[0], bucket[1])
  2028. values.append(milliseconds)
  2029. data = load_data("transaction")
  2030. data["transaction"] = "/failure_rate/sleepy_gary/{}".format(milliseconds)
  2031. data["timestamp"] = iso_format(start)
  2032. data["start_timestamp"] = (start - timedelta(milliseconds=milliseconds)).isoformat()
  2033. self.store_event(data, project_id=project.id)
  2034. # Add a transaction that totally throws off the buckets
  2035. milliseconds = random.randint(bucket[0], bucket[1])
  2036. data = load_data("transaction")
  2037. data["transaction"] = "/failure_rate/hamurai"
  2038. data["timestamp"] = iso_format(start)
  2039. data["start_timestamp"] = iso_format(start - timedelta(milliseconds=1000000))
  2040. self.store_event(data, project_id=project.id)
  2041. query = {
  2042. "field": ["histogram(transaction.duration, 10)", "count()"],
  2043. "query": "event.type:transaction transaction:/failure_rate/sleepy_gary*",
  2044. "sort": "histogram_transaction_duration_10",
  2045. }
  2046. features = {"organizations:discover-basic": True, "organizations:global-views": True}
  2047. response = self.do_request(query, features=features)
  2048. assert response.status_code == 200, response.content
  2049. data = response.data["data"]
  2050. assert len(data) == 11
  2051. bucket_size = ceil((max(values) - min(values)) / 10.0)
  2052. expected = [
  2053. (0, 5),
  2054. (bucket_size, 4),
  2055. (bucket_size * 2, 0),
  2056. (bucket_size * 3, 3),
  2057. (bucket_size * 4, 0),
  2058. (bucket_size * 5, 0),
  2059. (bucket_size * 6, 2),
  2060. (bucket_size * 7, 0),
  2061. (bucket_size * 8, 0),
  2062. (bucket_size * 9, 0),
  2063. (bucket_size * 10, 1),
  2064. ]
  2065. for idx, datum in enumerate(data):
  2066. assert datum["histogram_transaction_duration_10"] == expected[idx][0]
  2067. assert datum["count"] == expected[idx][1]
  2068. @mock.patch("sentry.utils.snuba.quantize_time")
  2069. def test_quantize_dates(self, mock_quantize):
  2070. self.create_project()
  2071. mock_quantize.return_value = before_now(days=1).replace(tzinfo=utc)
  2072. # Don't quantize short time periods
  2073. query = {"statsPeriod": "1h", "query": "", "field": ["id", "timestamp"]}
  2074. self.do_request(query)
  2075. # Don't quantize absolute date periods
  2076. self.do_request(query)
  2077. query = {
  2078. "start": iso_format(before_now(days=20)),
  2079. "end": iso_format(before_now(days=15)),
  2080. "query": "",
  2081. "field": ["id", "timestamp"],
  2082. }
  2083. self.do_request(query)
  2084. assert len(mock_quantize.mock_calls) == 0
  2085. # Quantize long date periods
  2086. query = {"field": ["id", "timestamp"], "statsPeriod": "90d", "query": ""}
  2087. self.do_request(query)
  2088. assert len(mock_quantize.mock_calls) == 2
  2089. def test_limit_number_of_fields(self):
  2090. self.create_project()
  2091. for i in range(1, 25):
  2092. response = self.do_request({"field": ["id"] * i})
  2093. if i <= 20:
  2094. assert response.status_code == 200
  2095. else:
  2096. assert response.status_code == 400
  2097. assert (
  2098. response.data["detail"]
  2099. == "You can view up to 20 fields at a time. Please delete some and try again."
  2100. )