Skip to content

Commit

Permalink
Merge branch 'master' into dry_run/metric/derived_code_mapping/armenzg
Browse files Browse the repository at this point in the history
  • Loading branch information
armenzg committed Mar 5, 2025
2 parents bf93f1d + 296272a commit 9d61289
Show file tree
Hide file tree
Showing 167 changed files with 3,371 additions and 2,546 deletions.
2 changes: 1 addition & 1 deletion migrations_lockfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ remote_subscriptions: 0003_drop_remote_subscription

replays: 0004_index_together

sentry: 0836_create_groupsearchviewstarred_table
sentry: 0837_create_groupsearchviewlastseen_table

social_auth: 0002_default_auto_field

Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@
"@sentry-internal/rrweb": "2.33.0",
"@sentry-internal/rrweb-player": "2.33.0",
"@sentry-internal/rrweb-snapshot": "2.33.0",
"@sentry/browser": "9.2.0-alpha.1",
"@sentry/core": "9.2.0-alpha.1",
"@sentry/node": "9.2.0-alpha.1",
"@sentry/react": "9.2.0-alpha.1",
"@sentry/browser": "9.5.0-alpha.0",
"@sentry/core": "9.5.0-alpha.0",
"@sentry/node": "9.5.0-alpha.0",
"@sentry/react": "9.5.0-alpha.0",
"@sentry/release-parser": "^1.3.1",
"@sentry/status-page-list": "^0.6.0",
"@sentry/webpack-plugin": "^3.1.1",
Expand Down Expand Up @@ -183,7 +183,7 @@
"@eslint/js": "^9.17.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.15",
"@sentry/jest-environment": "6.0.0",
"@sentry/profiling-node": "9.2.0-alpha.1",
"@sentry/profiling-node": "9.5.0-alpha.0",
"@styled/typescript-styled-plugin": "^1.0.1",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.6.3",
Expand Down
5 changes: 0 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,14 @@ ignore_missing_imports = true
[[tool.mypy.overrides]]
module = [
"sentry.api.base",
"sentry.api.bases.organization_events",
"sentry.api.endpoints.group_integration_details",
"sentry.api.endpoints.group_integrations",
"sentry.api.endpoints.organization_events_facets_performance",
"sentry.api.endpoints.organization_events_meta",
"sentry.api.endpoints.organization_events_spans_performance",
"sentry.api.endpoints.organization_member.details",
"sentry.api.endpoints.organization_projects",
"sentry.api.endpoints.organization_releases",
"sentry.api.endpoints.organization_request_project_creation",
"sentry.api.endpoints.organization_search_details",
"sentry.api.endpoints.project_index",
"sentry.api.endpoints.project_ownership",
"sentry.api.endpoints.project_repo_path_parsing",
"sentry.api.endpoints.project_rules_configuration",
Expand All @@ -140,7 +136,6 @@ module = [
"sentry.incidents.endpoints.bases",
"sentry.incidents.endpoints.organization_alert_rule_details",
"sentry.incidents.endpoints.organization_alert_rule_index",
"sentry.incidents.endpoints.organization_incident_index",
"sentry.integrations.aws_lambda.integration",
"sentry.integrations.bitbucket_server.integration",
"sentry.integrations.example.integration",
Expand Down
43 changes: 37 additions & 6 deletions src/sentry/api/bases/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from collections.abc import Sequence
from datetime import datetime
from typing import Any, TypedDict
from typing import Any, Literal, NotRequired, TypedDict, overload

import sentry_sdk
from django.core.cache import cache
Expand Down Expand Up @@ -305,14 +305,24 @@ def convert_args(
return (args, kwargs)


class FilterParams(TypedDict, total=False):
class FilterParams(TypedDict):
start: datetime | None
end: datetime | None
project_id: list[int]
project_objects: list[Project]
organization_id: int
environment: list[str] | None
environment_objects: list[Environment] | None
environment: NotRequired[list[str]]
environment_objects: NotRequired[list[Environment]]


class FilterParamsDateNotNull(TypedDict):
start: datetime
end: datetime
project_id: list[int]
project_objects: list[Project]
organization_id: int
environment: NotRequired[list[str]]
environment_objects: NotRequired[list[Environment]]


def _validate_fetched_projects(
Expand Down Expand Up @@ -461,14 +471,35 @@ def get_environments(
) -> list[Environment]:
return get_environments(request, organization)

@overload
def get_filter_params(
self,
request: Request,
organization: Organization | RpcOrganization,
date_filter_optional: bool = False,
project_ids: list[int] | set[int] | None = None,
project_slugs: list[str] | set[str] | None = None,
) -> FilterParams:
) -> FilterParamsDateNotNull: ...

@overload
def get_filter_params(
self,
request: Request,
organization: Organization | RpcOrganization,
project_ids: list[int] | set[int] | None = None,
project_slugs: list[str] | set[str] | None = None,
*,
date_filter_optional: Literal[True],
) -> FilterParams: ...

def get_filter_params(
self,
request: Request,
organization: Organization | RpcOrganization,
project_ids: list[int] | set[int] | None = None,
project_slugs: list[str] | set[str] | None = None,
*,
date_filter_optional: bool = False,
) -> FilterParams | FilterParamsDateNotNull:
"""
Extracts common filter parameters from the request and returns them
in a standard format.
Expand Down
24 changes: 15 additions & 9 deletions src/sentry/api/bases/organization_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from sentry.api.api_owners import ApiOwner
from sentry.api.base import CURSOR_LINK_HEADER
from sentry.api.bases import NoProjects
from sentry.api.bases.organization import OrganizationEndpoint
from sentry.api.bases.organization import FilterParamsDateNotNull, OrganizationEndpoint
from sentry.api.helpers.mobile import get_readable_device_name
from sentry.api.helpers.teams import get_teams
from sentry.api.serializers.snuba import BaseSnubaSerializer, SnubaTSResultSerializer
from sentry.api.serializers.snuba import SnubaTSResultSerializer
from sentry.api.utils import handle_query_errors
from sentry.discover.arithmetic import is_equation, strip_equation
from sentry.discover.models import DatasetSourcesTypes, DiscoverSavedQueryTypes
Expand All @@ -37,6 +37,7 @@
from sentry.snuba import discover
from sentry.snuba.metrics.extraction import MetricSpecType
from sentry.snuba.utils import DATASET_LABELS, DATASET_OPTIONS, get_dataset
from sentry.users.services.user.serial import serialize_generic_user
from sentry.utils import snuba
from sentry.utils.cursors import Cursor
from sentry.utils.dates import get_interval_from_range, get_rollup_from_request, parse_stats_period
Expand Down Expand Up @@ -129,15 +130,17 @@ def get_snuba_params(
detail=f"You can view up to {MAX_FIELDS} fields at a time. Please delete some and try again."
)

filter_params: dict[str, Any] = self.get_filter_params(request, organization)
filter_params = self.get_filter_params(request, organization)
if quantize_date_params:
filter_params = self.quantize_date_params(request, filter_params)
params = SnubaParams(
start=filter_params["start"],
end=filter_params["end"],
environments=filter_params.get("environment_objects", []),
projects=filter_params["project_objects"],
user=request.user if request.user else None,
user=serialize_generic_user(
request.user if request.user.is_authenticated else None
),
teams=self.get_teams(request, organization),
organization=organization,
)
Expand All @@ -163,7 +166,9 @@ def get_orderby(self, request: Request) -> list[str] | None:
return orderby
return None

def quantize_date_params(self, request: Request, params: dict[str, Any]) -> dict[str, Any]:
def quantize_date_params(
self, request: Request, params: FilterParamsDateNotNull
) -> FilterParamsDateNotNull:
# We only need to perform this rounding on relative date periods
if "statsPeriod" not in request.GET:
return params
Expand Down Expand Up @@ -418,7 +423,8 @@ def get_event_stats_data(
request: Request,
organization: Organization,
get_event_stats: Callable[
[list[str], str, SnubaParams, int, bool, timedelta | None], SnubaTSResult
[list[str], str, SnubaParams, int, bool, timedelta | None],
SnubaTSResult | dict[str, SnubaTSResult],
],
top_events: int = 0,
query_column: str = "count()",
Expand Down Expand Up @@ -466,7 +472,7 @@ def get_event_stats_data(
stats_period = parse_stats_period(get_interval_from_range(date_range, False))
rollup = int(stats_period.total_seconds()) if stats_period is not None else 3600
if comparison_delta is not None:
retention = quotas.get_event_retention(organization=organization)
retention = quotas.backend.get_event_retention(organization=organization)
comparison_start = snuba_params.start_date - comparison_delta
if retention and comparison_start < timezone.now() - timedelta(days=retention):
raise ValidationError("Comparison period is outside your retention window")
Expand All @@ -484,7 +490,7 @@ def get_event_stats_data(
# there were no top events found. In this case, result contains a zerofilled series
# that acts as a placeholder.
is_multiple_axis = len(query_columns) > 1
if top_events > 0 and isinstance(result, dict):
if isinstance(result, dict):
results = {}
for key, event_result in result.items():
if is_multiple_axis:
Expand Down Expand Up @@ -588,7 +594,7 @@ def serialize_multiple_axis(
self,
request: Request,
organization: Organization,
serializer: BaseSnubaSerializer,
serializer: SnubaTSResultSerializer,
event_result: SnubaTSResult,
snuba_params: SnubaParams,
columns: Sequence[str],
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/auth_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def post(self, request: Request) -> Response:
curl -X ###METHOD### -u username:password ###URL###
"""
if isinstance(request.user, AnonymousUser) or not request.user.is_authenticated:
if not request.user.is_authenticated:
return Response(status=status.HTTP_400_BAD_REQUEST)

if is_demo_user(request.user):
Expand Down
8 changes: 6 additions & 2 deletions src/sentry/api/endpoints/organization_member/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.db import router, transaction
from django.db.models import Q
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework import serializers
from rest_framework import serializers, status
from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request
from rest_framework.response import Response
Expand Down Expand Up @@ -199,6 +199,9 @@ def put(
For example, an organization Manager may change someone's role from
Member to Manager, but not to Owner.
"""
if not request.user.is_authenticated:
return Response(status=status.HTTP_400_BAD_REQUEST)

allowed_roles = get_allowed_org_roles(request, organization)
serializer = OrganizationMemberRequestSerializer(
data=request.data,
Expand Down Expand Up @@ -245,6 +248,7 @@ def put(
if not is_reinvite_request_only:
return Response({"detail": ERR_EDIT_WHEN_REINVITING}, status=403)
if member.is_pending:
assert member.email is not None
if ratelimits.for_organization_member_invite(
organization=organization,
email=member.email,
Expand All @@ -270,7 +274,7 @@ def put(
return Response({"detail": ERR_EXPIRED}, status=400)
member.send_invite_email()
elif auth_provider and not getattr(member.flags, "sso:linked"):
member.send_sso_link_email(request.user.id, auth_provider)
member.send_sso_link_email(request.user.email, auth_provider)
else:
# TODO(dcramer): proper error message
return Response({"detail": ERR_UNINVITABLE}, status=400)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework import serializers, status
from rest_framework.request import Request
from rest_framework.response import Response

Expand All @@ -26,6 +26,9 @@ def post(self, request: Request, organization) -> Response:
Send an email requesting a project be created
"""

if not request.user.is_authenticated:
return Response(status=status.HTTP_400_BAD_REQUEST)

serializer = OrganizationRequestProjectCreationSerializer(data=request.data)
if not serializer.is_valid():
return self.respond(serializer.errors, status=400)
Expand Down
10 changes: 6 additions & 4 deletions src/sentry/api/endpoints/project_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ def get(self, request: Request) -> Response:
queryset = queryset.none()

if request.auth and not request.user.is_authenticated:
if hasattr(request.auth, "project"):
if request.auth.project_id:
queryset = queryset.filter(id=request.auth.project_id)
elif request.auth.organization_id is not None:
queryset = queryset.filter(organization_id=request.auth.organization_id)
else:
queryset = queryset.none()
elif not (is_active_superuser(request) and request.GET.get("show") == "all"):
if request.user.is_sentry_app:
if request.user.is_authenticated and request.user.is_sentry_app:
queryset = SentryAppInstallation.objects.get_projects(request.auth)
if isinstance(queryset, EmptyQuerySet):
raise AuthenticationFailed("Token not found")
Expand All @@ -69,8 +69,10 @@ def get(self, request: Request) -> Response:
tokens = tokenize_query(query)
for key, value in tokens.items():
if key == "query":
value = " ".join(value)
queryset = queryset.filter(Q(name__icontains=value) | Q(slug__icontains=value))
value_s = " ".join(value)
queryset = queryset.filter(
Q(name__icontains=value_s) | Q(slug__icontains=value_s)
)
elif key == "slug":
queryset = queryset.filter(in_iexact("slug", value))
elif key == "name":
Expand Down
12 changes: 5 additions & 7 deletions src/sentry/api/serializers/snuba.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ def calculate_time_frame(start, end, rollup):
return {"start": rollup_start, "end": rollup_end}


class BaseSnubaSerializer:
class SnubaTSResultSerializer:
"""
Serializer for time-series Snuba data.
"""

def __init__(self, organization, lookup, user):
self.organization = organization
self.lookup = lookup
Expand All @@ -62,12 +66,6 @@ def get_attrs(self, item_list):

return self.lookup.serializer(self.organization, item_list, self.user)


class SnubaTSResultSerializer(BaseSnubaSerializer):
"""
Serializer for time-series Snuba data.
"""

def serialize(
self,
result,
Expand Down
3 changes: 3 additions & 0 deletions src/sentry/backup/comparators.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,9 @@ def get_default_comparators() -> dict[str, list[JSONScrubbingComparator]]:
DateUpdatedComparator("date_added", "date_updated"),
],
"sentry.groupsearchview": [DateUpdatedComparator("date_updated")],
"sentry.groupsearchviewlastvisited": [
DateUpdatedComparator("last_visited", "date_added", "date_updated")
],
"sentry.groupsearchviewstarred": [DateUpdatedComparator("date_updated", "date_added")],
"sentry.groupsearchviewproject": [
DateUpdatedComparator("date_updated"),
Expand Down
6 changes: 2 additions & 4 deletions src/sentry/grouping/ingest/grouphash_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from datetime import datetime
from typing import Any, TypeIs, cast

from sentry import features, options
from sentry import options
from sentry.eventstore.models import Event
from sentry.grouping.api import get_contributing_variant_and_component
from sentry.grouping.component import (
Expand Down Expand Up @@ -110,9 +110,7 @@

def should_handle_grouphash_metadata(project: Project, grouphash_is_new: bool) -> bool:
# Killswitches
if not options.get("grouping.grouphash_metadata.ingestion_writes_enabled") or not features.has(
"organizations:grouphash-metadata-creation", project.organization
):
if not options.get("grouping.grouphash_metadata.ingestion_writes_enabled"):
return False

# While we're backfilling metadata for existing grouphash records, if the load is too high, we
Expand Down
Loading

0 comments on commit 9d61289

Please sign in to comment.