Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(issue-states): Add ability to search for substatus #48289

Merged
merged 7 commits into from
May 3, 2023

Conversation

snigdhas
Copy link
Member

@snigdhas snigdhas commented May 1, 2023

Adds substatuses to the approved values for the is: search filter.

WOR-2898

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label May 1, 2023
@snigdhas snigdhas changed the title (wip) add substatuses to search feat(issue-states): Add ability to search for substatus May 1, 2023
@snigdhas snigdhas added this to the Issue States and Filters milestone May 1, 2023
@snigdhas snigdhas requested review from barkbarkimashark and a team May 1, 2023 22:34
@codecov
Copy link

codecov bot commented May 1, 2023

Codecov Report

Merging #48289 (382320e) into master (f086416) will increase coverage by 0.01%.
The diff coverage is 97.14%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #48289      +/-   ##
==========================================
+ Coverage   80.88%   80.90%   +0.01%     
==========================================
  Files        4770     4777       +7     
  Lines      201632   201877     +245     
  Branches    11557    11548       -9     
==========================================
+ Hits       163096   163324     +228     
- Misses      38280    38298      +18     
+ Partials      256      255       -1     
Impacted Files Coverage Δ
src/sentry/api/serializers/models/group.py 97.03% <ø> (ø)
static/app/utils/fields/index.ts 62.50% <ø> (ø)
static/app/views/replays/detail/network/index.tsx 0.00% <ø> (ø)
src/sentry/api/issue_search.py 97.11% <95.00%> (-0.54%) ⬇️
src/sentry/models/group.py 93.68% <100.00%> (+0.01%) ⬆️
src/sentry/search/utils.py 88.96% <100.00%> (+0.17%) ⬆️
src/sentry/types/group.py 100.00% <100.00%> (ø)

... and 161 files with indirect coverage changes

@snigdhas snigdhas marked this pull request as ready for review May 2, 2023 00:12
@snigdhas snigdhas requested a review from a team May 2, 2023 00:12
@snigdhas snigdhas requested a review from a team as a code owner May 2, 2023 00:12
@snigdhas snigdhas requested a review from a team May 2, 2023 00:12
Copy link
Member

@lobsterkatie lobsterkatie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few small questions and stylistic nits - feel free to do with them what you will.

Someone more familiar with this part of the codebase should also approve this, but just taking what's here at face value, overall LGTM!

Comment on lines 41 to +44
for status_key, status_value in STATUS_QUERY_CHOICES.items():
is_filter_translation[status_key] = ("status", status_value)

for substatus_key, substatus_value in SUBSTATUS_UPDATE_CHOICES.items():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar enough with this stuff to know: is there a reason one of these is query choices and one is update choices? Do they actually serve different purposes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I just ended up naming SUBSTATUS_UPDATE_CHOICES based on what I was using it for at the time. They should serve the same purpose.

Comment on lines 134 to 139
def convert_substatus_value(
value: Iterable[Union[str, int]],
projects: Sequence[Project],
user: User,
environments: Optional[Sequence[Environment]],
) -> List[int]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def convert_substatus_value(
value: Iterable[Union[str, int]],
projects: Sequence[Project],
user: User,
environments: Optional[Sequence[Environment]],
) -> List[int]:
def convert_substatus_value(
value: Iterable[str | int],
projects: Sequence[Project],
user: User,
environments: Sequence[Environment] | None,
) -> list[int]:

Supposedly the new-fangled way is preferred, though it's making me wonder - do you need a =None on environments if you use | None instead of Optional? The docs don't say so (and I know in JS you wouldn't) but do you in Python?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I agree with this answer - the Optional typing + default argument is easier to read than X | None = None

Copy link
Contributor

@barkbarkimashark barkbarkimashark May 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally like Optional vs <type> | None, but I've seen it done both ways throughout the codebase.

src/sentry/api/issue_search.py Outdated Show resolved Hide resolved
src/sentry/api/issue_search.py Outdated Show resolved Hide resolved
src/sentry/api/issue_search.py Outdated Show resolved Hide resolved
src/sentry/search/utils.py Outdated Show resolved Hide resolved
tests/sentry/api/test_issue_search.py Outdated Show resolved Hide resolved
tests/snuba/search/test_backend.py Outdated Show resolved Hide resolved
InvalidSearchQuery, match="The substatus filter is not supported for this organization"
):
self.make_query(search_filter_query="is:ongoing")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it legal to filter on incompatible status and substatus values, i.e. `search_filter_query="is:ongoing is:resolved"?

What about compatible combinations: search_filter_query="is:ongoing is:unresolved"?

I wonder what will happen with: search_filter_query="is:ongoing is:forever"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ticket should address that

if search_filter.key.name == "substatus":
if not features.has("organizations:issue-states", organization):
raise InvalidSearchQuery(
"The substatus filter is not supported for this organization"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sine this is behind a feature flag, should be return the same error message as if the filtering on substatus values doesn't exist:
Screenshot 2023-05-02 at 12 56 05 PM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way is fine with me - @robinrendle do you have a preference here?

Copy link
Contributor

@barkbarkimashark barkbarkimashark May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be easy enough to change the predicate to:

if search_filter.key.name == "substatus" and features.has("organizations:issue-states", organization):
       # do logic

and just let it fall through if the above is False.

)

status = GROUP_SUBSTATUS_TO_STATUS_MAP.get(
new_value[0] if isinstance(new_value, list) else new_value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure if grabbing the first substatus value here would be good UX. Should we raise an error instead or let the query go through?

Although, not sure if the is: query syntax even allows list values so we might be ok here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new_value is [0] when searching for is:unresolved. Not sure why..

Added error handling if the length is >1 though.

Copy link
Member

@wedamija wedamija left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh I wonder if we should have indexed on project, substatus, ... in retrospect. Each substatus only ever has one status, and it'd simplify this pr a bit

if value in STATUS_QUERY_CHOICES.values():
return int(value)
def parse_status_value(status: Union[str, int]) -> int:
if status in STATUS_QUERY_CHOICES.keys():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better off not using .keys here, this changes this from an O(1) operation to O(N)

@snigdhas snigdhas merged commit 484b4cf into master May 3, 2023
@snigdhas snigdhas deleted the snigdha/search-ongoing branch May 3, 2023 21:28
@github-actions github-actions bot locked and limited conversation to collaborators Jun 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Scope: Backend Automatically applied to PRs that change backend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants