Skip to content

Commit d4cd486

Browse files
authored
feat(aci): Add ability to filter workflows by connected detectors (#105695)
Adds the ability to do `/organizations/org-slug/workflows/?detector=1&detector=2` which will return workflows connected to either of those two detectors.
1 parent dbc19e4 commit d4cd486

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

src/sentry/workflow_engine/endpoints/organization_workflow_index.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ def filter_workflows(self, request: Request, organization: Organization) -> Quer
121121
# If specific IDs are provided, skip query and project filtering
122122
return queryset
123123

124+
if raw_detectorlist := request.GET.getlist("detector"):
125+
try:
126+
detector_ids = [int(id) for id in raw_detectorlist]
127+
except ValueError:
128+
raise ValidationError({"detector": ["Invalid detector ID format"]})
129+
queryset = queryset.filter(detectorworkflow__detector_id__in=detector_ids).distinct()
130+
124131
if raw_query := request.GET.get("query"):
125132
for filter in parse_workflow_query(raw_query):
126133
assert isinstance(filter, SearchFilter)

tests/sentry/workflow_engine/endpoints/test_organization_workflow_index.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ def test_simple(self) -> None:
6565
hits = int(response["X-Hits"])
6666
assert hits == 3
6767

68+
def test_only_returns_workflows_from_organization(self) -> None:
69+
other_org = self.create_organization()
70+
self.create_workflow(organization_id=other_org.id, name="Other Org Workflow")
71+
72+
response = self.get_success_response(self.organization.slug)
73+
assert len(response.data) == 3
74+
workflow_names = {w["name"] for w in response.data}
75+
assert "Other Org Workflow" not in workflow_names
76+
assert workflow_names == {
77+
self.workflow.name,
78+
self.workflow_two.name,
79+
self.workflow_three.name,
80+
}
81+
6882
def test_empty_result(self) -> None:
6983
response = self.get_success_response(
7084
self.organization.slug, qs_params={"query": "aaaaaaaaaaaaa"}
@@ -346,6 +360,53 @@ def test_query_filter_by_action(self) -> None:
346360
assert len(response3.data) == 2
347361
assert {self.workflow.name, self.workflow_two.name} == {w["name"] for w in response3.data}
348362

363+
def test_filter_by_detector(self) -> None:
364+
project_1 = self.create_project(organization=self.organization)
365+
project_2 = self.create_project(organization=self.organization)
366+
project_3 = self.create_project(organization=self.organization)
367+
368+
detector_1 = self.create_detector(project=project_1, name="Detector 1")
369+
detector_2 = self.create_detector(project=project_2, name="Detector 2")
370+
detector_3 = self.create_detector(project=project_3, name="Detector 3")
371+
372+
self.create_detector_workflow(workflow=self.workflow, detector=detector_1)
373+
self.create_detector_workflow(workflow=self.workflow_two, detector=detector_2)
374+
self.create_detector_workflow(workflow=self.workflow_three, detector=detector_3)
375+
376+
# Filter by single detector
377+
response = self.get_success_response(
378+
self.organization.slug,
379+
qs_params={"detector": str(detector_1.id)},
380+
)
381+
assert len(response.data) == 1
382+
assert response.data[0]["name"] == self.workflow.name
383+
384+
# Filter by multiple detectors
385+
response2 = self.get_success_response(
386+
self.organization.slug,
387+
qs_params=[
388+
("detector", str(detector_1.id)),
389+
("detector", str(detector_2.id)),
390+
],
391+
)
392+
assert len(response2.data) == 2
393+
assert {w["name"] for w in response2.data} == {self.workflow.name, self.workflow_two.name}
394+
395+
# Filter by non-existent detector ID returns no results
396+
response3 = self.get_success_response(
397+
self.organization.slug,
398+
qs_params={"detector": "999999"},
399+
)
400+
assert len(response3.data) == 0
401+
402+
# Invalid detector ID format returns error
403+
response4 = self.get_error_response(
404+
self.organization.slug,
405+
qs_params={"detector": "not-an-id"},
406+
status_code=400,
407+
)
408+
assert response4.data == {"detector": ["Invalid detector ID format"]}
409+
349410
def test_compound_query(self) -> None:
350411
self.create_detector_workflow(
351412
workflow=self.workflow, detector=self.create_detector(project=self.project)

0 commit comments

Comments
 (0)