|
29 | 29 | from sentry.issues import grouptype
|
30 | 30 | from sentry.models.organization import Organization
|
31 | 31 | from sentry.models.project import Project
|
| 32 | +from sentry.search.utils import parse_actor_or_none_value |
32 | 33 | from sentry.workflow_engine.endpoints.serializers import DetectorSerializer
|
33 | 34 | from sentry.workflow_engine.endpoints.utils.filters import apply_filter
|
34 | 35 | from sentry.workflow_engine.endpoints.utils.sortby import SortByParam
|
|
39 | 40 | detector_search_config = SearchConfig.create_from(
|
40 | 41 | default_config,
|
41 | 42 | text_operator_keys={"name", "type"},
|
42 |
| - allowed_keys={"name", "type"}, |
| 43 | + allowed_keys={"name", "type", "assignee"}, |
43 | 44 | allow_boolean=False,
|
44 | 45 | free_text_key="query",
|
45 | 46 | )
|
46 | 47 | parse_detector_query = partial(base_parse_search_query, config=detector_search_config)
|
47 | 48 |
|
| 49 | + |
| 50 | +def convert_assignee_value(value: str, projects: list[Project], user) -> Q: |
| 51 | + """ |
| 52 | + Convert an assignee search value to a Django Q object for filtering detectors. |
| 53 | + """ |
| 54 | + if value == "me": |
| 55 | + # Handle "me" keyword to search for current user |
| 56 | + return Q(owner_user_id=user.id) |
| 57 | + elif value == "unassigned": |
| 58 | + # Handle "unassigned" keyword |
| 59 | + return Q(owner_user_id__isnull=True, owner_team__isnull=True) |
| 60 | + else: |
| 61 | + # Parse actor (user or team) and create appropriate Q object |
| 62 | + actor = parse_actor_or_none_value(projects, value, user) |
| 63 | + if actor is None: |
| 64 | + # If we can't parse the actor, return a query that matches nothing |
| 65 | + return Q(pk__isnull=True) |
| 66 | + elif hasattr(actor, "id") and hasattr(actor, "_meta") and actor._meta.model_name == "user": |
| 67 | + # It's a user |
| 68 | + return Q(owner_user_id=actor.id) |
| 69 | + elif hasattr(actor, "id") and hasattr(actor, "_meta") and actor._meta.model_name == "team": |
| 70 | + # It's a team |
| 71 | + return Q(owner_team_id=actor.id) |
| 72 | + else: |
| 73 | + # Unknown actor type, return a query that matches nothing |
| 74 | + return Q(pk__isnull=True) |
| 75 | + |
| 76 | + |
48 | 77 | # Maps API field name to database field name, with synthetic aggregate fields keeping
|
49 | 78 | # to our field naming scheme for consistency.
|
50 | 79 | SORT_ATTRS = {
|
@@ -134,6 +163,24 @@ def get(self, request: Request, organization: Organization) -> Response:
|
134 | 163 | queryset = apply_filter(queryset, filter, "name")
|
135 | 164 | case SearchFilter(key=SearchKey("type"), operator=("=" | "IN" | "!=")):
|
136 | 165 | queryset = apply_filter(queryset, filter, "type")
|
| 166 | + case SearchFilter(key=SearchKey("assignee"), operator=("=" | "IN" | "!=")): |
| 167 | + breakpoint() |
| 168 | + # Handle assignee filtering with support for users, teams, "me", and "unassigned" |
| 169 | + if isinstance(filter.value.value, list): |
| 170 | + # Handle multiple values (IN operator) |
| 171 | + assignee_q = Q() |
| 172 | + for value in filter.value.value: |
| 173 | + assignee_q |= convert_assignee_value(value, projects, request.user) |
| 174 | + else: |
| 175 | + # Handle single value |
| 176 | + assignee_q = convert_assignee_value( |
| 177 | + filter.value.value, projects, request.user |
| 178 | + ) |
| 179 | + |
| 180 | + if filter.operator == "!=": |
| 181 | + queryset = queryset.exclude(assignee_q) |
| 182 | + else: |
| 183 | + queryset = queryset.filter(assignee_q) |
137 | 184 | case SearchFilter(key=SearchKey("query"), operator="="):
|
138 | 185 | # 'query' is our free text key; all free text gets returned here
|
139 | 186 | # as '=', and we search any relevant fields for it.
|
|
0 commit comments