12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336 |
- from __future__ import absolute_import
- import six
- import random
- import mock
- from pytz import utc
- from datetime import timedelta
- from math import ceil
- from django.core.urlresolvers import reverse
- from sentry.testutils import APITestCase, SnubaTestCase
- from sentry.testutils.helpers import parse_link_header
- from sentry.testutils.helpers.datetime import before_now, iso_format
- from sentry.utils.samples import load_data
- from sentry.utils.compat.mock import patch
- from sentry.utils.snuba import (
- RateLimitExceeded,
- QueryIllegalTypeOfArgument,
- QueryExecutionError,
- )
- class OrganizationEventsV2EndpointTest(APITestCase, SnubaTestCase):
- def setUp(self):
- super(OrganizationEventsV2EndpointTest, self).setUp()
- self.min_ago = iso_format(before_now(minutes=1))
- self.two_min_ago = iso_format(before_now(minutes=2))
- def do_request(self, query, features=None):
- if features is None:
- features = {"organizations:discover-basic": True}
- self.login_as(user=self.user)
- url = reverse(
- "sentry-api-0-organization-eventsv2",
- kwargs={"organization_slug": self.organization.slug},
- )
- with self.feature(features):
- return self.client.get(url, query, format="json")
- def test_no_projects(self):
- response = self.do_request({})
- assert response.status_code == 200, response.content
- assert len(response.data) == 0
- def test_performance_view_feature(self):
- self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.min_ago, "fingerprint": ["group1"]},
- project_id=self.project.id,
- )
- query = {"field": ["id", "project.id"], "project": [self.project.id]}
- response = self.do_request(query)
- assert response.status_code == 200
- assert len(response.data["data"]) == 1
- def test_multi_project_feature_gate_rejection(self):
- team = self.create_team(organization=self.organization, members=[self.user])
- project = self.create_project(organization=self.organization, teams=[team])
- project2 = self.create_project(organization=self.organization, teams=[team])
- self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.min_ago, "fingerprint": ["group1"]},
- project_id=project.id,
- )
- self.store_event(
- data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group2"]},
- project_id=project2.id,
- )
- query = {"field": ["id", "project.id"], "project": [project.id, project2.id]}
- response = self.do_request(query)
- assert response.status_code == 400
- assert "events from multiple projects" in response.data["detail"]
- def test_invalid_search_terms(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "message": "how to make fast", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {"field": ["id"], "query": "hi \n there"}
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert (
- response.data["detail"]
- == "Parse error at 'hi \n ther' (column 4). This is commonly caused by unmatched parentheses. Enclose any text in double quotes."
- )
- @patch("sentry.snuba.discover.raw_query")
- def test_handling_snuba_errors(self, mock_query):
- mock_query.side_effect = RateLimitExceeded("test")
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "message": "how to make fast"}, project_id=project.id
- )
- query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert (
- response.data["detail"]
- == "Query timeout. Please try again. If the problem persists try a smaller date range or fewer projects."
- )
- mock_query.side_effect = QueryExecutionError("test")
- query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert response.data["detail"] == "Internal error. Your query failed to run."
- mock_query.side_effect = QueryIllegalTypeOfArgument("test")
- query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert response.data["detail"] == "Invalid query. Argument to function is wrong type."
- def test_out_of_retention(self):
- self.create_project()
- with self.options({"system.event-retention-days": 10}):
- query = {
- "field": ["id", "timestamp"],
- "orderby": ["-timestamp", "-id"],
- "start": iso_format(before_now(days=20)),
- "end": iso_format(before_now(days=15)),
- }
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert response.data["detail"] == "Invalid date range. Please try a more recent date range."
- def test_raw_data(self):
- project = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "environment": "staging",
- "timestamp": self.two_min_ago,
- "user": {"ip_address": "127.0.0.1", "email": "foo@example.com"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "environment": "staging",
- "timestamp": self.min_ago,
- "user": {"ip_address": "127.0.0.1", "email": "foo@example.com"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["id", "project.id", "user.email", "user.ip", "timestamp"],
- "orderby": "-timestamp",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 2
- assert data[0]["id"] == "b" * 32
- assert data[0]["project.id"] == project.id
- assert data[0]["user.email"] == "foo@example.com"
- assert "project.name" not in data[0], "project.id does not auto select name"
- assert "project" not in data[0]
- meta = response.data["meta"]
- assert meta["id"] == "string"
- assert meta["user.email"] == "string"
- assert meta["user.ip"] == "string"
- assert meta["timestamp"] == "date"
- def test_project_name(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {"field": ["project.name", "environment"]}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["project.name"] == project.slug
- assert "project.id" not in response.data["data"][0]
- assert response.data["data"][0]["environment"] == "staging"
- def test_project_without_name(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {"field": ["project", "environment"]}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["project"] == project.slug
- assert response.data["meta"]["project"] == "string"
- assert "project.id" not in response.data["data"][0]
- assert response.data["data"][0]["environment"] == "staging"
- def test_project_in_query(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {
- "field": ["project", "count()"],
- "query": 'project:"%s"' % project.slug,
- "statsPeriod": "14d",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["project"] == project.slug
- assert "project.id" not in response.data["data"][0]
- def test_project_in_query_not_in_header(self):
- project = self.create_project()
- other_project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {
- "field": ["project", "count()"],
- "query": 'project:"%s"' % project.slug,
- "statsPeriod": "14d",
- "project": other_project.id,
- }
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert (
- response.data["detail"]
- == "Invalid query. Project %s does not exist or is not an actively selected project."
- % project.slug
- )
- def test_project_in_query_does_not_exist(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {
- "field": ["project", "count()"],
- "query": "project:morty",
- "statsPeriod": "14d",
- }
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert (
- response.data["detail"]
- == "Invalid query. Project morty does not exist or is not an actively selected project."
- )
- def test_project_condition_used_for_automatic_filters(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {
- "field": ["project", "count()"],
- "query": 'project:"%s"' % project.slug,
- "statsPeriod": "14d",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["project"] == project.slug
- assert "project.id" not in response.data["data"][0]
- def test_user_search(self):
- project = self.create_project()
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["user"] = {
- "email": "foo@example.com",
- "id": "123",
- "ip_address": "127.0.0.1",
- "username": "foo",
- }
- self.store_event(data, project_id=project.id)
- fields = {
- "email": "user.email",
- "id": "user.id",
- "ip_address": "user.ip",
- "username": "user.username",
- }
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- for key, value in data["user"].items():
- field = fields[key]
- query = {
- "field": ["project", "user"],
- "query": "{}:{}".format(field, value),
- "statsPeriod": "14d",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["project"] == project.slug
- assert response.data["data"][0]["user"] == "id:123"
- def test_has_user(self):
- project = self.create_project()
- data = load_data("transaction", timestamp=before_now(minutes=1))
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- for value in data["user"].values():
- query = {"field": ["project", "user"], "query": "has:user", "statsPeriod": "14d"}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["user"] == "ip:{}".format(data["user"]["ip_address"])
- def test_has_issue(self):
- project = self.create_project()
- event = self.store_event(
- {"timestamp": iso_format(before_now(minutes=1))}, project_id=project.id
- )
- data = load_data("transaction", timestamp=before_now(minutes=1))
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {"field": ["project", "issue"], "query": "has:issue", "statsPeriod": "14d"}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["issue"] == event.group.qualified_short_id
- query = {"field": ["project", "issue"], "query": "!has:issue", "statsPeriod": "14d"}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["issue"] == "unknown"
- def test_negative_user_search(self):
- project = self.create_project()
- user_data = {"email": "foo@example.com", "id": "123", "username": "foo"}
- # Load an event with data that shouldn't match
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/transactions/nomatch"
- event_user = user_data.copy()
- event_user["id"] = "undefined"
- data["user"] = event_user
- self.store_event(data, project_id=project.id)
- # Load a matching event
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/transactions/matching"
- data["user"] = user_data
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["project", "user"],
- "query": '!user:"id:undefined"',
- "statsPeriod": "14d",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["user"] == "id:{}".format(user_data["id"])
- assert "user.email" not in response.data["data"][0]
- assert "user.id" not in response.data["data"][0]
- def test_not_project_in_query(self):
- project1 = self.create_project()
- project2 = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project1.id,
- )
- self.store_event(
- data={"event_id": "b" * 32, "environment": "staging", "timestamp": self.min_ago},
- project_id=project2.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["project", "count()"],
- "query": '!project:"%s"' % project1.slug,
- "statsPeriod": "14d",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["data"][0]["project"] == project2.slug
- assert "project.id" not in response.data["data"][0]
- def test_implicit_groupby(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
- project_id=project.id,
- )
- event1 = self.store_event(
- data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group_1"]},
- project_id=project.id,
- )
- event2 = self.store_event(
- data={"event_id": "c" * 32, "timestamp": self.min_ago, "fingerprint": ["group_2"]},
- project_id=project.id,
- )
- query = {"field": ["count(id)", "project.id", "issue.id"], "orderby": "issue.id"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0] == {
- "project.id": project.id,
- "issue.id": event1.group_id,
- "count_id": 2,
- }
- assert data[1] == {
- "project.id": project.id,
- "issue.id": event2.group_id,
- "count_id": 1,
- }
- meta = response.data["meta"]
- assert meta["count_id"] == "integer"
- def test_orderby(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.two_min_ago}, project_id=project.id
- )
- self.store_event(
- data={"event_id": "b" * 32, "timestamp": self.min_ago}, project_id=project.id
- )
- self.store_event(
- data={"event_id": "c" * 32, "timestamp": self.min_ago}, project_id=project.id
- )
- query = {"field": ["id", "timestamp"], "orderby": ["-timestamp", "-id"]}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert data[0]["id"] == "c" * 32
- assert data[1]["id"] == "b" * 32
- assert data[2]["id"] == "a" * 32
- def test_sort_title(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "message": "zlast", "timestamp": self.two_min_ago},
- project_id=project.id,
- )
- self.store_event(
- data={"event_id": "b" * 32, "message": "second", "timestamp": self.min_ago},
- project_id=project.id,
- )
- self.store_event(
- data={"event_id": "c" * 32, "message": "first", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {"field": ["id", "title"], "sort": "title"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert data[0]["id"] == "c" * 32
- assert data[1]["id"] == "b" * 32
- assert data[2]["id"] == "a" * 32
- def test_sort_invalid(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.two_min_ago}, project_id=project.id
- )
- query = {"field": ["id"], "sort": "garbage"}
- response = self.do_request(query)
- assert response.status_code == 400
- assert "order by" in response.content
- def test_latest_release_alias(self):
- project = self.create_project()
- event1 = self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "release": "0.8"},
- project_id=project.id,
- )
- query = {"field": ["issue.id", "release"], "query": "release:latest"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert data[0]["issue.id"] == event1.group_id
- assert data[0]["release"] == "0.8"
- event2 = self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.min_ago, "release": "0.9"},
- project_id=project.id,
- )
- query = {"field": ["issue.id", "release"], "query": "release:latest"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert data[0]["issue.id"] == event2.group_id
- assert data[0]["release"] == "0.9"
- def test_aliased_fields(self):
- project = self.create_project()
- event1 = self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"email": "foo@example.com"},
- },
- project_id=project.id,
- )
- event2 = self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "bar@example.com"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["issue.id", "count(id)", "count_unique(user)"],
- "orderby": "issue.id",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0]["issue.id"] == event1.group_id
- assert data[0]["count_id"] == 1
- assert data[0]["count_unique_user"] == 1
- assert "projectid" not in data[0]
- assert "project.id" not in data[0]
- assert data[1]["issue.id"] == event2.group_id
- assert data[1]["count_id"] == 2
- assert data[1]["count_unique_user"] == 2
- def test_aggregate_field_with_dotted_param(self):
- project = self.create_project()
- event1 = self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"id": "123", "email": "foo@example.com"},
- },
- project_id=project.id,
- )
- event2 = self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"id": "123", "email": "foo@example.com"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"id": "456", "email": "bar@example.com"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["issue.id", "issue_title", "count(id)", "count_unique(user.email)"],
- "orderby": "issue.id",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0]["issue.id"] == event1.group_id
- assert data[0]["count_id"] == 1
- assert data[0]["count_unique_user_email"] == 1
- assert "projectid" not in data[0]
- assert "project.id" not in data[0]
- assert data[1]["issue.id"] == event2.group_id
- assert data[1]["count_id"] == 2
- assert data[1]["count_unique_user_email"] == 2
- def test_failure_rate_alias_field(self):
- project = self.create_project()
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/failure_rate/success"
- self.store_event(data, project_id=project.id)
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/failure_rate/unknown"
- data["contexts"]["trace"]["status"] = "unknown_error"
- self.store_event(data, project_id=project.id)
- for i in range(6):
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/failure_rate/{}".format(i)
- data["contexts"]["trace"]["status"] = "unauthenticated"
- self.store_event(data, project_id=project.id)
- query = {"field": ["failure_rate()"], "query": "event.type:transaction"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["failure_rate"] == 0.75
- def test_user_misery_alias_field(self):
- project = self.create_project()
- events = [
- ("one", 300),
- ("one", 300),
- ("two", 3000),
- ("two", 3000),
- ("three", 300),
- ("three", 3000),
- ]
- for idx, event in enumerate(events):
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=(1 + idx)),
- start_timestamp=before_now(minutes=(1 + idx), milliseconds=event[1]),
- )
- data["event_id"] = "{}".format(idx) * 32
- data["transaction"] = "/user_misery/horribilis/{}".format(idx)
- data["user"] = {"email": "{}@example.com".format(event[0])}
- self.store_event(data, project_id=project.id)
- query = {"field": ["user_misery(300)"], "query": "event.type:transaction"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["user_misery_300"] == 2
- def test_aggregation(self):
- project = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- "tags": {"sub_customer.is-Enterprise-42": "1"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "staging",
- "tags": {"sub_customer.is-Enterprise-42": "1"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- "tags": {"sub_customer.is-Enterprise-42": "0"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "d" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- "tags": {"sub_customer.is-Enterprise-42": "1"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["sub_customer.is-Enterprise-42", "count(sub_customer.is-Enterprise-42)"],
- "orderby": "sub_customer.is-Enterprise-42",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0]["count_sub_customer_is-Enterprise-42"] == 1
- assert data[1]["count_sub_customer_is-Enterprise-42"] == 3
- def test_aggregation_comparison(self):
- project = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"email": "foo@example.com"},
- },
- project_id=project.id,
- )
- event = self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "bar@example.com"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "d" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_3"],
- "user": {"email": "bar@example.com"},
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "e" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_3"],
- "user": {"email": "bar@example.com"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["issue.id", "count(id)", "count_unique(user)"],
- "query": "count(id):>1 count_unique(user):>1",
- "orderby": "issue.id",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["issue.id"] == event.group_id
- assert data[0]["count_id"] == 2
- assert data[0]["count_unique_user"] == 2
- def test_aggregation_alias_comparison(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/aggregates/1"
- self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=3),
- )
- data["transaction"] = "/aggregates/2"
- event = self.store_event(data, project_id=project.id)
- query = {
- "field": ["transaction", "p95()"],
- "query": "event.type:transaction p95():<4000",
- "orderby": ["transaction"],
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["transaction"] == event.transaction
- assert data[0]["p95"] == 3000
- def test_aggregation_comparison_with_conditions(self):
- project = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "staging",
- },
- project_id=project.id,
- )
- event = self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "d" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- query = {
- "field": ["issue.id", "count(id)"],
- "query": "count(id):>1 user.email:foo@example.com environment:prod",
- "orderby": "issue.id",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["issue.id"] == event.group_id
- assert data[0]["count_id"] == 2
- def test_aggregation_date_comparison_with_conditions(self):
- project = self.create_project()
- event = self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "staging",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "d" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- query = {
- "field": ["issue.id", "max(timestamp)"],
- "query": "max(timestamp):>1 user.email:foo@example.com environment:prod",
- "orderby": "issue.id",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- response.data["meta"]["max_timestamp"] == "date"
- data = response.data["data"]
- assert data[0]["issue.id"] == event.group_id
- def test_percentile_function(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/aggregates/1"
- event1 = self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=3),
- )
- data["transaction"] = "/aggregates/2"
- event2 = self.store_event(data, project_id=project.id)
- query = {
- "field": ["transaction", "percentile(transaction.duration, 0.95)"],
- "query": "event.type:transaction",
- "orderby": ["transaction"],
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0]["transaction"] == event1.transaction
- assert data[0]["percentile_transaction_duration_0_95"] == 5000
- assert data[1]["transaction"] == event2.transaction
- assert data[1]["percentile_transaction_duration_0_95"] == 3000
- def test_percentile_function_as_condition(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/aggregates/1"
- event1 = self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=3),
- )
- data["transaction"] = "/aggregates/2"
- self.store_event(data, project_id=project.id)
- query = {
- "field": ["transaction", "percentile(transaction.duration, 0.95)"],
- "query": "event.type:transaction percentile(transaction.duration, 0.95):>4000",
- "orderby": ["transaction"],
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["transaction"] == event1.transaction
- assert data[0]["percentile_transaction_duration_0_95"] == 5000
- def test_epm_function(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/aggregates/1"
- event1 = self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=3),
- )
- data["transaction"] = "/aggregates/2"
- event2 = self.store_event(data, project_id=project.id)
- query = {
- "field": ["transaction", "epm()"],
- "query": "event.type:transaction",
- "orderby": ["transaction"],
- "statsPeriod": "2m",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0]["transaction"] == event1.transaction
- assert data[0]["epm"] == 0.5
- assert data[1]["transaction"] == event2.transaction
- assert data[1]["epm"] == 0.5
- def test_nonexistent_fields(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "message": "how to make fast", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {"field": ["issue_world.id"]}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert response.data["data"][0]["issue_world.id"] == ""
- def test_no_requested_fields_or_grouping(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "message": "how to make fast", "timestamp": self.min_ago},
- project_id=project.id,
- )
- query = {"query": "test"}
- response = self.do_request(query)
- assert response.status_code == 400, response.content
- assert response.data["detail"] == "No columns selected"
- def test_condition_on_aggregate_misses(self):
- project = self.create_project()
- self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "bar@example.com"},
- },
- project_id=project.id,
- )
- query = {"field": ["issue.id"], "query": "event_count:>0", "orderby": "issue.id"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 0
- def test_next_prev_link_headers(self):
- project = self.create_project()
- events = [("a", "group_1"), ("b", "group_2"), ("c", "group_2"), ("d", "group_2")]
- for e in events:
- self.store_event(
- data={
- "event_id": e[0] * 32,
- "timestamp": self.min_ago,
- "fingerprint": [e[1]],
- "user": {"email": "foo@example.com"},
- "tags": {"language": "C++"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["count(id)", "issue.id", "context.key"],
- "sort": "-count_id",
- "query": "language:C++",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- links = parse_link_header(response["Link"])
- for link in links:
- assert "field=issue.id" in link
- assert "field=count%28id%29" in link
- assert "field=context.key" in link
- assert "sort=-count_id" in link
- assert "query=language%3AC%2B%2B" in link
- assert len(response.data["data"]) == 2
- data = response.data["data"]
- assert data[0]["count_id"] == 3
- assert data[1]["count_id"] == 1
- def test_empty_count_query(self):
- project = self.create_project()
- event = self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": iso_format(before_now(minutes=5)),
- "fingerprint": ["1123581321"],
- "user": {"email": "foo@example.com"},
- "tags": {"language": "C++"},
- },
- project_id=project.id,
- )
- query = {
- "field": ["count()"],
- "query": "issue.id:%d timestamp:>%s" % (event.group_id, self.min_ago),
- "statsPeriod": "14d",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count"] == 0
- def test_reference_event(self):
- project = self.create_project()
- reference = self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "how to make more faster?",
- "timestamp": self.min_ago,
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "c" * 32,
- "transaction": "/nomatch",
- "message": "how to make fast",
- "timestamp": self.min_ago,
- },
- project_id=project.id,
- )
- query = {
- "field": ["transaction", "count()"],
- "query": "",
- "referenceEvent": "{}:{}".format(project.slug, reference.event_id),
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["transaction"] == "/example"
- def test_stack_wildcard_condition(self):
- project = self.create_project()
- data = load_data("javascript")
- data["timestamp"] = self.min_ago
- self.store_event(data=data, project_id=project.id)
- query = {"field": ["stack.filename", "message"], "query": "stack.filename:*.js"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["meta"]["message"] == "string"
- def test_email_wildcard_condition(self):
- project = self.create_project()
- data = load_data("javascript")
- data["timestamp"] = self.min_ago
- self.store_event(data=data, project_id=project.id)
- query = {"field": ["stack.filename", "message"], "query": "user.email:*@example.org"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["meta"]["message"] == "string"
- def test_transaction_event_type(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- self.store_event(data=data, project_id=project.id)
- query = {
- "field": ["transaction", "transaction.duration", "transaction.status"],
- "query": "event.type:transaction",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["meta"]["transaction.duration"] == "duration"
- assert response.data["meta"]["transaction.status"] == "string"
- assert response.data["data"][0]["transaction.status"] == "ok"
- def test_trace_columns(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- self.store_event(data=data, project_id=project.id)
- query = {"field": ["trace"], "query": "event.type:transaction"}
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- assert response.data["meta"]["trace"] == "string"
- assert response.data["data"][0]["trace"] == data["contexts"]["trace"]["trace_id"]
- def test_issue_in_columns(self):
- project1 = self.create_project()
- project2 = self.create_project()
- event1 = self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project1.id,
- )
- event2 = self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project2.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {"field": ["id", "issue"], "orderby": ["id"]}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 2
- assert data[0]["id"] == event1.event_id
- assert data[0]["issue.id"] == event1.group_id
- assert data[0]["issue"] == event1.group.qualified_short_id
- assert data[1]["id"] == event2.event_id
- assert data[1]["issue.id"] == event2.group_id
- assert data[1]["issue"] == event2.group.qualified_short_id
- def test_issue_in_search_and_columns(self):
- project1 = self.create_project()
- project2 = self.create_project()
- event1 = self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project1.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project2.id,
- )
- tests = [
- ("issue", "issue:%s" % event1.group.qualified_short_id),
- ("issue.id", "issue:%s" % event1.group.qualified_short_id),
- ("issue", "issue.id:%s" % event1.group_id),
- ("issue.id", "issue.id:%s" % event1.group_id),
- ]
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- for testdata in tests:
- query = {"field": [testdata[0]], "query": testdata[1]}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["id"] == event1.event_id
- assert data[0]["issue.id"] == event1.group_id
- if testdata[0] == "issue":
- assert data[0]["issue"] == event1.group.qualified_short_id
- else:
- assert data[0].get("issue", None) is None
- def test_issue_negation(self):
- project1 = self.create_project()
- project2 = self.create_project()
- event1 = self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project1.id,
- )
- event2 = self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "go really fast plz",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_2"],
- },
- project_id=project2.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["title", "issue.id"],
- "query": "!issue:{}".format(event1.group.qualified_short_id),
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["title"] == event2.title
- assert data[0]["issue.id"] == event2.group_id
- def test_search_for_nonexistent_issue(self):
- project1 = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project1.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {"field": ["count()"], "query": "issue.id:112358"}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count"] == 0
- def test_issue_alias_inside_aggregate(self):
- project1 = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project1.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_2"],
- },
- project_id=project1.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["project", "count(id)", "count_unique(issue.id)", "count_unique(issue)"],
- "sort": "-count(id)",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_id"] == 2
- assert data[0]["count_unique_issue_id"] == 2
- assert data[0]["count_unique_issue"] == 2
- def test_project_alias_inside_aggregate(self):
- project1 = self.create_project()
- project2 = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project1.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_2"],
- },
- project_id=project2.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": [
- "event.type",
- "count(id)",
- "count_unique(project.id)",
- "count_unique(project)",
- ],
- "sort": "-count(id)",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_id"] == 2
- assert data[0]["count_unique_project_id"] == 2
- assert data[0]["count_unique_project"] == 2
- def test_user_display(self):
- project1 = self.create_project()
- project2 = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "user": {"email": "cathy@example.com"},
- },
- project_id=project1.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "user": {"username": "catherine"},
- },
- project_id=project2.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["event.type", "user.display"],
- "query": "user.display:cath*",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 2
- result = set([r["user.display"] for r in data])
- assert result == set(["catherine", "cathy@example.com"])
- def test_user_display_with_aggregates(self):
- self.login_as(user=self.user)
- project1 = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "transaction": "/example",
- "message": "how to make fast",
- "timestamp": self.two_min_ago,
- "user": {"email": "cathy@example.com"},
- },
- project_id=project1.id,
- )
- with self.feature(
- {"organizations:discover-basic": True, "organizations:global-views": True}
- ):
- response = self.client.get(
- self.url,
- format="json",
- data={
- "field": ["event.type", "user.display", "count_unique(title)"],
- "statsPeriod": "24h",
- },
- )
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- result = set([r["user.display"] for r in data])
- assert result == set(["cathy@example.com"])
- with self.feature(
- {"organizations:discover-basic": True, "organizations:global-views": True}
- ):
- response = self.client.get(
- self.url,
- format="json",
- data={"field": ["event.type", "count_unique(user.display)"], "statsPeriod": "24h"},
- )
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_unique_user_display"] == 1
- def test_has_transaction_status(self):
- project = self.create_project()
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/transactionstatus/1"
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["event.type", "count(id)"],
- "query": "event.type:transaction has:transaction.status",
- "sort": "-count(id)",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_id"] == 1
- def test_not_has_transaction_status(self):
- project = self.create_project()
- data = load_data("transaction", timestamp=before_now(minutes=1))
- data["transaction"] = "/transactionstatus/1"
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["event.type", "count(id)"],
- "query": "event.type:transaction !has:transaction.status",
- "sort": "-count(id)",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_id"] == 0
- def test_tag_that_looks_like_aggregation(self):
- project = self.create_project()
- data = {
- "message": "Failure state",
- "timestamp": self.two_min_ago,
- "tags": {"count_diff": 99},
- }
- self.store_event(data, project_id=project.id)
- query = {
- "field": ["message", "count_diff", "count()"],
- "query": "",
- "project": [project.id],
- "statsPeriod": "24h",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- meta = response.data["meta"]
- assert "string" == meta["count_diff"], "tags should not be counted as integers"
- assert "string" == meta["message"]
- assert "integer" == meta["count"]
- assert 1 == len(response.data["data"])
- data = response.data["data"][0]
- assert "99" == data["count_diff"]
- assert "Failure state" == data["message"]
- assert 1 == data["count"]
- def test_aggregate_negation(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["event.type", "p99()"],
- "query": "event.type:transaction p99():5s",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- query = {
- "field": ["event.type", "p99()"],
- "query": "event.type:transaction !p99():5s",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 0
- def test_all_aggregates_in_columns(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=2),
- start_timestamp=before_now(minutes=2, seconds=5),
- )
- data["transaction"] = "/failure_rate/1"
- self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/failure_rate/1"
- data["contexts"]["trace"]["status"] = "unauthenticated"
- event = self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": [
- "event.type",
- "p50()",
- "p75()",
- "p95()",
- "p99()",
- "p100()",
- "percentile(transaction.duration, 0.99)",
- "apdex(300)",
- "user_misery(300)",
- "failure_rate()",
- ],
- "query": "event.type:transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- meta = response.data["meta"]
- assert meta["p50"] == "duration"
- assert meta["p75"] == "duration"
- assert meta["p95"] == "duration"
- assert meta["p99"] == "duration"
- assert meta["p100"] == "duration"
- assert meta["percentile_transaction_duration_0_99"] == "duration"
- assert meta["apdex_300"] == "number"
- assert meta["failure_rate"] == "percentage"
- assert meta["user_misery_300"] == "number"
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["p50"] == 5000
- assert data[0]["p75"] == 5000
- assert data[0]["p95"] == 5000
- assert data[0]["p99"] == 5000
- assert data[0]["p100"] == 5000
- assert data[0]["percentile_transaction_duration_0_99"] == 5000
- assert data[0]["apdex_300"] == 0.0
- assert data[0]["user_misery_300"] == 1
- assert data[0]["failure_rate"] == 0.5
- query = {
- "field": ["event.type", "last_seen()", "latest_event()"],
- "query": "event.type:transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert iso_format(before_now(minutes=1))[:-5] in data[0]["last_seen"]
- assert data[0]["latest_event"] == event.event_id
- query = {
- "field": [
- "event.type",
- "count()",
- "count(id)",
- "count_unique(project)",
- "min(transaction.duration)",
- "max(transaction.duration)",
- "avg(transaction.duration)",
- "sum(transaction.duration)",
- ],
- "query": "event.type:transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count"] == 2
- assert data[0]["count_id"] == 2
- assert data[0]["count_unique_project"] == 1
- assert data[0]["min_transaction_duration"] == 5000
- assert data[0]["max_transaction_duration"] == 5000
- assert data[0]["avg_transaction_duration"] == 5000
- assert data[0]["sum_transaction_duration"] == 10000
- def test_all_aggregates_in_query(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=2),
- start_timestamp=before_now(minutes=2, seconds=5),
- )
- data["transaction"] = "/failure_rate/1"
- self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/failure_rate/2"
- data["contexts"]["trace"]["status"] = "unauthenticated"
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": [
- "event.type",
- "p50()",
- "p75()",
- "p95()",
- "percentile(transaction.duration, 0.99)",
- "p100()",
- ],
- "query": "event.type:transaction p50():>100 p75():>1000 p95():>1000 p100():>1000 percentile(transaction.duration, 0.99):>1000",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["p50"] == 5000
- assert data[0]["p75"] == 5000
- assert data[0]["p95"] == 5000
- assert data[0]["p100"] == 5000
- assert data[0]["percentile_transaction_duration_0_99"] == 5000
- query = {
- "field": ["event.type", "apdex(300)", "user_misery(300)", "failure_rate()"],
- "query": "event.type:transaction apdex(300):>-1.0 failure_rate():>0.25",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["apdex_300"] == 0.0
- assert data[0]["user_misery_300"] == 1
- assert data[0]["failure_rate"] == 0.5
- query = {
- "field": ["event.type", "last_seen()", "latest_event()"],
- "query": u"event.type:transaction last_seen():>1990-12-01T00:00:00",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- query = {
- "field": ["event.type", "count()", "count(id)", "count_unique(transaction)"],
- "query": "event.type:transaction count():>1 count(id):>1 count_unique(transaction):>1",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count"] == 2
- assert data[0]["count_id"] == 2
- assert data[0]["count_unique_transaction"] == 2
- query = {
- "field": [
- "event.type",
- "min(transaction.duration)",
- "max(transaction.duration)",
- "avg(transaction.duration)",
- "sum(transaction.duration)",
- ],
- "query": "event.type:transaction min(transaction.duration):>1000 max(transaction.duration):>1000 avg(transaction.duration):>1000 sum(transaction.duration):>1000",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["min_transaction_duration"] == 5000
- assert data[0]["max_transaction_duration"] == 5000
- assert data[0]["avg_transaction_duration"] == 5000
- assert data[0]["sum_transaction_duration"] == 10000
- query = {
- "field": ["event.type", "apdex(400)"],
- "query": "event.type:transaction apdex(400):0",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["apdex_400"] == 0
- def test_functions_in_orderby(self):
- project = self.create_project()
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=2),
- start_timestamp=before_now(minutes=2, seconds=5),
- )
- data["transaction"] = "/failure_rate/1"
- self.store_event(data, project_id=project.id)
- data = load_data(
- "transaction",
- timestamp=before_now(minutes=1),
- start_timestamp=before_now(minutes=1, seconds=5),
- )
- data["transaction"] = "/failure_rate/2"
- data["contexts"]["trace"]["status"] = "unauthenticated"
- event = self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["event.type", "p75()"],
- "sort": "-p75",
- "query": "event.type:transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["p75"] == 5000
- query = {
- "field": ["event.type", "percentile(transaction.duration, 0.99)"],
- "sort": "-percentile_transaction_duration_0_99",
- "query": "event.type:transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["percentile_transaction_duration_0_99"] == 5000
- query = {
- "field": ["event.type", "apdex(300)"],
- "sort": "-apdex(300)",
- "query": "event.type:transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["apdex_300"] == 0.0
- query = {
- "field": ["event.type", "latest_event()"],
- "query": u"event.type:transaction",
- "sort": "latest_event",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["latest_event"] == event.event_id
- query = {
- "field": ["event.type", "count_unique(transaction)"],
- "query": "event.type:transaction",
- "sort": "-count_unique_transaction",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_unique_transaction"] == 2
- query = {
- "field": ["event.type", "min(transaction.duration)"],
- "query": "event.type:transaction",
- "sort": "-min_transaction_duration",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["min_transaction_duration"] == 5000
- def test_issue_alias_in_aggregate(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
- project_id=project.id,
- )
- self.store_event(
- data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group_2"]},
- project_id=project.id,
- )
- query = {
- "field": ["event.type", "count_unique(issue)"],
- "query": "count_unique(issue):>1",
- }
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["count_unique_issue"] == 2
- def test_deleted_issue_in_results(self):
- project = self.create_project()
- event1 = self.store_event(
- data={"event_id": "a" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
- project_id=project.id,
- )
- event2 = self.store_event(
- data={"event_id": "b" * 32, "timestamp": self.min_ago, "fingerprint": ["group_2"]},
- project_id=project.id,
- )
- event2.group.delete()
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {"field": ["issue", "count()"], "sort": "issue"}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 2
- assert data[0]["issue"] == event1.group.qualified_short_id
- assert data[1]["issue"] == "unknown"
- def test_last_seen_negative_duration(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "f" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
- project_id=project.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {"field": ["id", "last_seen()"], "query": "last_seen():-30d"}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["id"] == "f" * 32
- def test_last_seen_aggregate_condition(self):
- project = self.create_project()
- self.store_event(
- data={"event_id": "f" * 32, "timestamp": self.two_min_ago, "fingerprint": ["group_1"]},
- project_id=project.id,
- )
- query = {
- "field": ["id", "last_seen()"],
- "query": "last_seen():>{}".format(iso_format(before_now(days=30))),
- }
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 1
- assert data[0]["id"] == "f" * 32
- def test_conditional_filter(self):
- project = self.create_project()
- for v in ["a", "b"]:
- self.store_event(
- data={
- "event_id": v * 32,
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project.id,
- )
- query = {
- "field": ["id"],
- "query": "id:{} OR id:{}".format("a" * 32, "b" * 32),
- "orderby": "id",
- }
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 2
- assert data[0]["id"] == "a" * 32
- assert data[1]["id"] == "b" * 32
- def test_aggregation_comparison_with_conditional_filter(self):
- project = self.create_project()
- self.store_event(
- data={
- "event_id": "a" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_1"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "b" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "staging",
- },
- project_id=project.id,
- )
- event = self.store_event(
- data={
- "event_id": "c" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "prod",
- },
- project_id=project.id,
- )
- self.store_event(
- data={
- "event_id": "d" * 32,
- "timestamp": self.min_ago,
- "fingerprint": ["group_2"],
- "user": {"email": "foo@example.com"},
- "environment": "canary",
- },
- project_id=project.id,
- )
- query = {
- "field": ["issue.id", "count(id)"],
- "query": "count(id):>1 user.email:foo@example.com AND (environment:prod OR environment:staging)",
- "orderby": "issue.id",
- }
- response = self.do_request(query)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1
- data = response.data["data"]
- assert data[0]["issue.id"] == event.group_id
- assert data[0]["count_id"] == 2
- def test_messed_up_function_values(self):
- # TODO (evanh): It would be nice if this surfaced an error to the user.
- # The problem: The && causes the parser to treat that term not as a bad
- # function call but a valid raw search with parens in it. It's not trivial
- # to change the parser to recognize "bad function values" and surface them.
- project = self.create_project()
- for v in ["a", "b"]:
- self.store_event(
- data={
- "event_id": v * 32,
- "timestamp": self.two_min_ago,
- "fingerprint": ["group_1"],
- },
- project_id=project.id,
- )
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": [
- "transaction",
- "project",
- "epm()",
- "p50()",
- "p95()",
- "failure_rate()",
- "apdex(300)",
- "count_unique(user)",
- "user_misery(300)",
- ],
- "query": "failure_rate():>0.003&& users:>10 event.type:transaction",
- "sort": "-failure_rate",
- "statsPeriod": "24h",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 0
- def test_context_fields_between_datasets(self):
- project = self.create_project()
- event_data = load_data("android")
- transaction_data = load_data("transaction")
- event_data["spans"] = transaction_data["spans"]
- event_data["contexts"]["trace"] = transaction_data["contexts"]["trace"]
- event_data["type"] = "transaction"
- event_data["transaction"] = "/failure_rate/1"
- event_data["timestamp"] = iso_format(before_now(minutes=1))
- event_data["start_timestamp"] = iso_format(before_now(minutes=1, seconds=5))
- event_data["user"]["geo"] = {"country_code": "US", "region": "CA", "city": "San Francisco"}
- event_data["contexts"]["http"] = {
- "method": "GET",
- "referer": "something.something",
- "url": "https://areyouasimulation.com",
- }
- self.store_event(event_data, project_id=project.id)
- event_data["type"] = "error"
- self.store_event(event_data, project_id=project.id)
- fields = [
- "http.method",
- "http.referer",
- "http.url",
- "os.build",
- "os.kernel_version",
- "device.arch",
- "device.battery_level",
- "device.brand",
- "device.charging",
- "device.locale",
- "device.model_id",
- "device.name",
- "device.online",
- "device.orientation",
- "device.simulator",
- "device.uuid",
- ]
- data = [
- {"field": fields + ["location", "count()"], "query": "event.type:error"},
- {"field": fields + ["duration", "count()"], "query": "event.type:transaction"},
- ]
- for datum in data:
- response = self.do_request(datum)
- assert response.status_code == 200, response.content
- assert len(response.data["data"]) == 1, datum
- results = response.data["data"]
- assert results[0]["count"] == 1, datum
- for field in fields:
- key, value = field.split(".", 1)
- expected = six.text_type(event_data["contexts"][key][value])
- assert results[0][field] == expected, field + six.text_type(datum)
- def test_histogram_function(self):
- project = self.create_project()
- start = before_now(minutes=2).replace(microsecond=0)
- latencies = [
- (1, 500, 5),
- (1000, 1500, 4),
- (3000, 3500, 3),
- (6000, 6500, 2),
- (10000, 10000, 1), # just to make the math easy
- ]
- values = []
- for bucket in latencies:
- for i in range(bucket[2]):
- # Don't generate a wide range of variance as the buckets can mis-align.
- milliseconds = random.randint(bucket[0], bucket[1])
- values.append(milliseconds)
- data = load_data("transaction")
- data["transaction"] = "/failure_rate/{}".format(milliseconds)
- data["timestamp"] = iso_format(start)
- data["start_timestamp"] = (start - timedelta(milliseconds=milliseconds)).isoformat()
- self.store_event(data, project_id=project.id)
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- query = {
- "field": ["histogram(transaction.duration, 10)", "count()"],
- "query": "event.type:transaction",
- "sort": "histogram_transaction_duration_10",
- }
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 11
- bucket_size = ceil((max(values) - min(values)) / 10.0)
- expected = [
- (0, 5),
- (bucket_size, 4),
- (bucket_size * 2, 0),
- (bucket_size * 3, 3),
- (bucket_size * 4, 0),
- (bucket_size * 5, 0),
- (bucket_size * 6, 2),
- (bucket_size * 7, 0),
- (bucket_size * 8, 0),
- (bucket_size * 9, 0),
- (bucket_size * 10, 1),
- ]
- for idx, datum in enumerate(data):
- assert datum["histogram_transaction_duration_10"] == expected[idx][0]
- assert datum["count"] == expected[idx][1]
- def test_histogram_function_with_filters(self):
- project = self.create_project()
- start = before_now(minutes=2).replace(microsecond=0)
- latencies = [
- (1, 500, 5),
- (1000, 1500, 4),
- (3000, 3500, 3),
- (6000, 6500, 2),
- (10000, 10000, 1), # just to make the math easy
- ]
- values = []
- for bucket in latencies:
- for i in range(bucket[2]):
- milliseconds = random.randint(bucket[0], bucket[1])
- values.append(milliseconds)
- data = load_data("transaction")
- data["transaction"] = "/failure_rate/sleepy_gary/{}".format(milliseconds)
- data["timestamp"] = iso_format(start)
- data["start_timestamp"] = (start - timedelta(milliseconds=milliseconds)).isoformat()
- self.store_event(data, project_id=project.id)
- # Add a transaction that totally throws off the buckets
- milliseconds = random.randint(bucket[0], bucket[1])
- data = load_data("transaction")
- data["transaction"] = "/failure_rate/hamurai"
- data["timestamp"] = iso_format(start)
- data["start_timestamp"] = iso_format(start - timedelta(milliseconds=1000000))
- self.store_event(data, project_id=project.id)
- query = {
- "field": ["histogram(transaction.duration, 10)", "count()"],
- "query": "event.type:transaction transaction:/failure_rate/sleepy_gary*",
- "sort": "histogram_transaction_duration_10",
- }
- features = {"organizations:discover-basic": True, "organizations:global-views": True}
- response = self.do_request(query, features=features)
- assert response.status_code == 200, response.content
- data = response.data["data"]
- assert len(data) == 11
- bucket_size = ceil((max(values) - min(values)) / 10.0)
- expected = [
- (0, 5),
- (bucket_size, 4),
- (bucket_size * 2, 0),
- (bucket_size * 3, 3),
- (bucket_size * 4, 0),
- (bucket_size * 5, 0),
- (bucket_size * 6, 2),
- (bucket_size * 7, 0),
- (bucket_size * 8, 0),
- (bucket_size * 9, 0),
- (bucket_size * 10, 1),
- ]
- for idx, datum in enumerate(data):
- assert datum["histogram_transaction_duration_10"] == expected[idx][0]
- assert datum["count"] == expected[idx][1]
- @mock.patch("sentry.utils.snuba.quantize_time")
- def test_quantize_dates(self, mock_quantize):
- self.create_project()
- mock_quantize.return_value = before_now(days=1).replace(tzinfo=utc)
- # Don't quantize short time periods
- query = {"statsPeriod": "1h", "query": "", "field": ["id", "timestamp"]}
- self.do_request(query)
- # Don't quantize absolute date periods
- self.do_request(query)
- query = {
- "start": iso_format(before_now(days=20)),
- "end": iso_format(before_now(days=15)),
- "query": "",
- "field": ["id", "timestamp"],
- }
- self.do_request(query)
- assert len(mock_quantize.mock_calls) == 0
- # Quantize long date periods
- query = {"field": ["id", "timestamp"], "statsPeriod": "90d", "query": ""}
- self.do_request(query)
- assert len(mock_quantize.mock_calls) == 2
- def test_limit_number_of_fields(self):
- self.create_project()
- for i in range(1, 25):
- response = self.do_request({"field": ["id"] * i})
- if i <= 20:
- assert response.status_code == 200
- else:
- assert response.status_code == 400
- assert (
- response.data["detail"]
- == "You can view up to 20 fields at a time. Please delete some and try again."
- )
|