Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/sentry/api/endpoints/organization_releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@
MergingOffsetPaginator,
OffsetPaginator,
)
from sentry.api.release_search import FINALIZED_KEY, RELEASE_FREE_TEXT_KEY, parse_search_query
from sentry.api.release_search import (
FINALIZED_KEY,
RELEASE_CREATED_KEY,
RELEASE_FREE_TEXT_KEY,
parse_search_query,
)
from sentry.api.serializers import serialize
from sentry.api.serializers.rest_framework import (
ReleaseHeadCommitSerializer,
Expand Down Expand Up @@ -166,6 +171,13 @@ def _filter_releases_by_query(queryset, organization, query, filter_params):
negated=negated,
)

if search_filter.key.name == RELEASE_CREATED_KEY:
queryset = queryset.filter(
**{
f"date_added__{OPERATOR_TO_DJANGO[search_filter.operator]}": search_filter.value.raw_value
}
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Missing negation handling causes KeyError with != operator

The RELEASE_CREATED_KEY filter directly accesses OPERATOR_TO_DJANGO[search_filter.operator], but OPERATOR_TO_DJANGO does not include the != operator. When a user queries with release.created:!=<date>, this will raise a KeyError. The adjacent SEMVER_BUILD_ALIAS filter correctly handles this by calling handle_operator_negation() first to convert != to = and extract a negated flag, then using exclude() when negated.

Fix in Cursor Fix in Web

Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Missing negation handling causes KeyError with != operator

The RELEASE_CREATED_KEY filter directly accesses OPERATOR_TO_DJANGO[search_filter.operator], but OPERATOR_TO_DJANGO does not include the != operator. When a user queries with release.created:!=<date>, this will raise a KeyError. The adjacent SEMVER_BUILD_ALIAS filter correctly handles this by calling handle_operator_negation() first to convert != to = and extract a negated flag, then using exclude() when negated.

Fix in Cursor Fix in Web


return queryset


Expand Down
3 changes: 3 additions & 0 deletions src/sentry/api/release_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

RELEASE_FREE_TEXT_KEY = "release_free_text"
FINALIZED_KEY = "finalized"
RELEASE_CREATED_KEY = "release.created"
INVALID_SEMVER_MESSAGE = (
'Invalid format of semantic version. For searching non-semver releases, use "release:" instead.'
)
Expand All @@ -25,7 +26,9 @@
SEMVER_BUILD_ALIAS,
SEMVER_PACKAGE_ALIAS,
FINALIZED_KEY,
RELEASE_CREATED_KEY,
},
date_keys={RELEASE_CREATED_KEY},
allow_boolean=False,
free_text_key=RELEASE_FREE_TEXT_KEY,
)
Expand Down
41 changes: 40 additions & 1 deletion tests/sentry/api/endpoints/test_organization_releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.utils import timezone

from sentry.api.endpoints.organization_releases import ReleaseSerializerWithProjects
from sentry.api.release_search import FINALIZED_KEY
from sentry.api.release_search import FINALIZED_KEY, RELEASE_CREATED_KEY
from sentry.api.serializers.rest_framework.release import ReleaseHeadCommitSerializer
from sentry.auth import access
from sentry.constants import BAD_RELEASE_CHARS, MAX_COMMIT_LENGTH, MAX_VERSION_LENGTH
Expand Down Expand Up @@ -1097,6 +1097,45 @@ def test_finalized_filter(self) -> None:
release_1.version,
]

def test_release_created_filter(self) -> None:
self.login_as(user=self.user)

now = timezone.now()
one_day_ago = now - timedelta(days=1)
one_week_ago = now - timedelta(days=7)
two_weeks_ago = now - timedelta(days=14)

release_1 = self.create_release(version="release-1", date_added=two_weeks_ago)
release_2 = self.create_release(version="release-2", date_added=one_week_ago)
release_3 = self.create_release(version="release-3", date_added=one_day_ago)
release_4 = self.create_release(version="release-4", date_added=now)

# Test relative date filter: releases created in the last 3 days
response = self.get_success_response(
self.organization.slug, query=f"{RELEASE_CREATED_KEY}:-3d"
)
assert [r["version"] for r in response.data] == [
release_4.version,
release_3.version,
]

# Test comparison operator: releases created after a specific date
formatted_date = one_week_ago.strftime("%Y-%m-%dT%H:%M:%S")
response = self.get_success_response(
self.organization.slug, query=f"{RELEASE_CREATED_KEY}:>={formatted_date}"
)
assert [r["version"] for r in response.data] == [
release_4.version,
release_3.version,
release_2.version,
]

# Test comparison operator: releases created before a specific date
response = self.get_success_response(
self.organization.slug, query=f"{RELEASE_CREATED_KEY}:<{formatted_date}"
)
assert [r["version"] for r in response.data] == [release_1.version]

def test_release_stage_filter(self) -> None:
self.login_as(user=self.user)

Expand Down
Loading