test_discover_saved_query_detail.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. import pytest
  2. from django.urls import NoReverseMatch, reverse
  3. from sentry.discover.models import (
  4. DiscoverSavedQuery,
  5. DiscoverSavedQueryProject,
  6. DiscoverSavedQueryTypes,
  7. )
  8. from sentry.testutils.cases import APITestCase, SnubaTestCase
  9. class DiscoverSavedQueryDetailTest(APITestCase, SnubaTestCase):
  10. feature_name = "organizations:discover"
  11. def setUp(self):
  12. super().setUp()
  13. self.login_as(user=self.user)
  14. self.org = self.create_organization(owner=self.user)
  15. self.org_without_access = self.create_organization()
  16. self.project_ids = [
  17. self.create_project(organization=self.org).id,
  18. self.create_project(organization=self.org).id,
  19. ]
  20. query = {"fields": ["test"], "conditions": [], "limit": 10}
  21. model = DiscoverSavedQuery.objects.create(
  22. organization=self.org, created_by_id=self.user.id, name="Test query", query=query
  23. )
  24. model.set_projects(self.project_ids)
  25. self.query_id = model.id
  26. invalid = DiscoverSavedQuery.objects.create(
  27. organization=self.org_without_access, name="Query without access", query=query
  28. )
  29. invalid.set_projects(self.project_ids)
  30. self.query_id_without_access = invalid.id
  31. def test_invalid_id(self):
  32. with pytest.raises(NoReverseMatch):
  33. reverse("sentry-api-0-discover-saved-query-detail", args=[self.org.slug, "not-an-id"])
  34. def test_get(self):
  35. with self.feature(self.feature_name):
  36. url = reverse(
  37. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  38. )
  39. response = self.client.get(url)
  40. assert response.status_code == 200, response.content
  41. assert response.data["id"] == str(self.query_id)
  42. assert set(response.data["projects"]) == set(self.project_ids)
  43. assert response.data["fields"] == ["test"]
  44. assert response.data["conditions"] == []
  45. assert response.data["limit"] == 10
  46. def test_get_discover_query_flag(self):
  47. with self.feature("organizations:discover-query"):
  48. url = reverse(
  49. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  50. )
  51. response = self.client.get(url)
  52. assert response.status_code == 200, response.content
  53. assert response.data["id"] == str(self.query_id)
  54. assert set(response.data["projects"]) == set(self.project_ids)
  55. assert response.data["fields"] == ["test"]
  56. assert response.data["conditions"] == []
  57. assert response.data["limit"] == 10
  58. def test_get_version(self):
  59. query = {"fields": ["event_id"], "query": "event.type:error", "limit": 10, "version": 2}
  60. model = DiscoverSavedQuery.objects.create(
  61. organization=self.org, created_by_id=self.user.id, name="v2 query", query=query
  62. )
  63. model.set_projects(self.project_ids)
  64. with self.feature(self.feature_name):
  65. url = reverse(
  66. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, model.id]
  67. )
  68. response = self.client.get(url)
  69. assert response.status_code == 200, response.content
  70. assert response.data["id"] == str(model.id)
  71. assert set(response.data["projects"]) == set(self.project_ids)
  72. assert response.data["fields"] == ["event_id"]
  73. assert response.data["query"] == "event.type:error"
  74. assert response.data["limit"] == 10
  75. assert response.data["version"] == 2
  76. def test_get_org_without_access(self):
  77. with self.feature(self.feature_name):
  78. url = reverse(
  79. "sentry-api-0-discover-saved-query-detail",
  80. args=[self.org_without_access.slug, self.query_id],
  81. )
  82. response = self.client.get(url)
  83. assert response.status_code == 403, response.content
  84. def test_get_homepage_query(self):
  85. query = {"fields": ["event_id"], "query": "event.type:error", "limit": 10, "version": 2}
  86. model = DiscoverSavedQuery.objects.create(
  87. organization=self.org,
  88. created_by_id=self.user.id,
  89. name="v2 query",
  90. query=query,
  91. is_homepage=True,
  92. )
  93. model.set_projects(self.project_ids)
  94. with self.feature(self.feature_name):
  95. url = reverse(
  96. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, model.id]
  97. )
  98. response = self.client.get(url)
  99. assert response.status_code == 404, response.content
  100. def test_put(self):
  101. with self.feature(self.feature_name):
  102. url = reverse(
  103. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  104. )
  105. response = self.client.put(
  106. url,
  107. {
  108. "name": "New query",
  109. "projects": self.project_ids,
  110. "fields": [],
  111. "range": "24h",
  112. "limit": 20,
  113. "conditions": [],
  114. "aggregations": [],
  115. "orderby": "-time",
  116. },
  117. )
  118. assert response.status_code == 200, response.content
  119. assert response.data["id"] == str(self.query_id)
  120. assert set(response.data["projects"]) == set(self.project_ids)
  121. assert response.data["fields"] == []
  122. assert response.data["conditions"] == []
  123. assert response.data["limit"] == 20
  124. def test_put_dataset(self):
  125. with self.feature(self.feature_name):
  126. url = reverse(
  127. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  128. )
  129. response = self.client.put(
  130. url,
  131. {
  132. "name": "New query",
  133. "projects": self.project_ids,
  134. "fields": [],
  135. "range": "24h",
  136. "limit": 20,
  137. "conditions": [],
  138. "aggregations": [],
  139. "orderby": "-time",
  140. "queryDataset": "transaction-like",
  141. },
  142. )
  143. assert response.status_code == 200, response.content
  144. assert response.data["id"] == str(self.query_id)
  145. assert set(response.data["projects"]) == set(self.project_ids)
  146. assert response.data["fields"] == []
  147. assert response.data["conditions"] == []
  148. assert response.data["limit"] == 20
  149. assert response.data["queryDataset"] == "transaction-like"
  150. def test_dataset_set_to_discover_on_update(self):
  151. query = {"fields": ["event_id"], "query": "event.type:error", "limit": 10, "version": 2}
  152. model = DiscoverSavedQuery.objects.create(
  153. organization=self.org,
  154. created_by_id=self.user.id,
  155. name="query",
  156. query=query,
  157. dataset=DiscoverSavedQueryTypes.TRANSACTION_LIKE,
  158. )
  159. with self.feature(self.feature_name):
  160. url = reverse(
  161. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, model.id]
  162. )
  163. response = self.client.put(
  164. url,
  165. {
  166. "name": "New query",
  167. "projects": self.project_ids,
  168. "fields": [],
  169. "range": "24h",
  170. "limit": 20,
  171. "conditions": [],
  172. "aggregations": [],
  173. "orderby": "-time",
  174. },
  175. )
  176. assert response.status_code == 200, response.content
  177. assert response.data["id"] == str(model.id)
  178. assert response.data["queryDataset"] == "discover"
  179. def test_put_with_interval(self):
  180. with self.feature(self.feature_name):
  181. url = reverse(
  182. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  183. )
  184. response = self.client.put(
  185. url,
  186. {
  187. "name": "New query",
  188. "projects": self.project_ids,
  189. "fields": ["transaction", "count()"],
  190. "range": "24h",
  191. "interval": "10m",
  192. "version": 2,
  193. "orderby": "-count",
  194. },
  195. )
  196. assert response.status_code == 200, response.content
  197. assert response.data["fields"] == ["transaction", "count()"]
  198. assert response.data["interval"] == "10m"
  199. def test_put_query_without_access(self):
  200. with self.feature(self.feature_name):
  201. url = reverse(
  202. "sentry-api-0-discover-saved-query-detail",
  203. args=[self.org.slug, self.query_id_without_access],
  204. )
  205. response = self.client.put(
  206. url, {"name": "New query", "projects": self.project_ids, "range": "24h"}
  207. )
  208. assert response.status_code == 404
  209. def test_put_query_with_team(self):
  210. team = self.create_team(organization=self.org, members=[self.user])
  211. project = self.create_project(organization=self.org, teams=[team])
  212. query = DiscoverSavedQuery.objects.create(
  213. organization=self.org,
  214. created_by_id=self.user.id,
  215. name="Test query",
  216. query={"fields": ["test"], "conditions": [], "limit": 10},
  217. )
  218. query.set_projects([project.id])
  219. with self.feature(self.feature_name):
  220. url = reverse(
  221. "sentry-api-0-discover-saved-query-detail",
  222. args=[self.org.slug, query.id],
  223. )
  224. response = self.client.put(url, {"name": "New query", "projects": [], "range": "24h"})
  225. assert response.status_code == 200
  226. def test_put_query_without_team(self):
  227. team = self.create_team(organization=self.org, members=[])
  228. project = self.create_project(organization=self.org, teams=[team])
  229. query = DiscoverSavedQuery.objects.create(
  230. organization=self.org,
  231. created_by_id=self.user.id,
  232. name="Test query",
  233. query={"fields": ["test"], "conditions": [], "limit": 10},
  234. )
  235. query.set_projects([project.id])
  236. with self.feature(self.feature_name):
  237. url = reverse(
  238. "sentry-api-0-discover-saved-query-detail",
  239. args=[self.org.slug, query.id],
  240. )
  241. response = self.client.put(url, {"name": "New query", "projects": [], "range": "24h"})
  242. assert response.status_code == 400
  243. assert "No Projects found, join a Team" == response.data["detail"]
  244. def test_put_homepage_query(self):
  245. query = {"fields": ["event_id"], "query": "event.type:error", "limit": 10, "version": 2}
  246. model = DiscoverSavedQuery.objects.create(
  247. organization=self.org,
  248. created_by_id=self.user.id,
  249. name="v2 query",
  250. query=query,
  251. is_homepage=True,
  252. )
  253. model.set_projects(self.project_ids)
  254. with self.feature(self.feature_name):
  255. url = reverse(
  256. "sentry-api-0-discover-saved-query-detail",
  257. args=[self.org.slug, model.id],
  258. )
  259. response = self.client.put(
  260. url, {"name": "New query", "projects": [], "range": "24h", "fields": []}
  261. )
  262. assert response.status_code == 404, response.content
  263. def test_put_org_without_access(self):
  264. with self.feature(self.feature_name):
  265. url = reverse(
  266. "sentry-api-0-discover-saved-query-detail",
  267. args=[self.org_without_access.slug, self.query_id],
  268. )
  269. response = self.client.put(
  270. url, {"name": "New query", "projects": self.project_ids, "range": "24h"}
  271. )
  272. assert response.status_code == 403, response.content
  273. def test_delete(self):
  274. with self.feature(self.feature_name):
  275. url = reverse(
  276. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  277. )
  278. response = self.client.delete(url)
  279. assert response.status_code == 204
  280. assert self.client.get(url).status_code == 404
  281. def test_delete_removes_projects(self):
  282. with self.feature(self.feature_name):
  283. url = reverse(
  284. "sentry-api-0-discover-saved-query-detail", args=[self.org.slug, self.query_id]
  285. )
  286. self.client.delete(url)
  287. projects = list(
  288. DiscoverSavedQueryProject.objects.filter(discover_saved_query=self.query_id)
  289. )
  290. assert projects == []
  291. def test_delete_query_without_access(self):
  292. with self.feature(self.feature_name):
  293. url = reverse(
  294. "sentry-api-0-discover-saved-query-detail",
  295. args=[self.org.slug, self.query_id_without_access],
  296. )
  297. response = self.client.delete(url)
  298. assert response.status_code == 404
  299. def test_delete_org_without_access(self):
  300. with self.feature(self.feature_name):
  301. url = reverse(
  302. "sentry-api-0-discover-saved-query-detail",
  303. args=[self.org_without_access.slug, self.query_id],
  304. )
  305. response = self.client.delete(url)
  306. assert response.status_code == 403, response.content
  307. def test_delete_homepage_query(self):
  308. query = {"fields": ["event_id"], "query": "event.type:error", "limit": 10, "version": 2}
  309. model = DiscoverSavedQuery.objects.create(
  310. organization=self.org,
  311. created_by_id=self.user.id,
  312. name="v2 query",
  313. query=query,
  314. is_homepage=True,
  315. )
  316. model.set_projects(self.project_ids)
  317. with self.feature(self.feature_name):
  318. url = reverse(
  319. "sentry-api-0-discover-saved-query-detail",
  320. args=[self.org.slug, model.id],
  321. )
  322. response = self.client.delete(url)
  323. assert response.status_code == 404, response.content
  324. class OrganizationDiscoverQueryVisitTest(APITestCase, SnubaTestCase):
  325. def setUp(self):
  326. super().setUp()
  327. self.login_as(user=self.user)
  328. self.org = self.create_organization(owner=self.user)
  329. self.org_without_access = self.create_organization()
  330. self.project_ids = [
  331. self.create_project(organization=self.org).id,
  332. self.create_project(organization=self.org).id,
  333. ]
  334. q = {"fields": ["test"], "conditions": [], "limit": 10}
  335. self.query = DiscoverSavedQuery.objects.create(
  336. organization=self.org, created_by_id=self.user.id, name="Test query", query=q
  337. )
  338. self.query.set_projects(self.project_ids)
  339. def url(self, query_id):
  340. return reverse(
  341. "sentry-api-0-discover-saved-query-visit",
  342. kwargs={"organization_id_or_slug": self.org.slug, "query_id": query_id},
  343. )
  344. def test_visit_query(self):
  345. last_visited = self.query.last_visited
  346. assert self.query.visits == 1
  347. with self.feature("organizations:discover-query"):
  348. response = self.client.post(self.url(self.query.id))
  349. assert response.status_code == 204
  350. query = DiscoverSavedQuery.objects.get(id=self.query.id)
  351. assert query.visits == 2
  352. assert query.last_visited > last_visited
  353. def test_visit_query_no_access(self):
  354. last_visited = self.query.last_visited
  355. assert self.query.visits == 1
  356. with self.feature({"organizations:discover-query": False}):
  357. response = self.client.post(self.url(self.query.id))
  358. assert response.status_code == 404
  359. query = DiscoverSavedQuery.objects.get(id=self.query.id)
  360. assert query.visits == 1
  361. assert query.last_visited == last_visited