Skip to content

Commit

Permalink
Merge branch 'priscila/feat/quick-start/add-logic-to-skeleton-part-2'…
Browse files Browse the repository at this point in the history
… of github.com:getsentry/sentry into priscila/feat/quick-start/add-logic-to-skeleton-part-2
  • Loading branch information
priscilawebdev committed Oct 17, 2024
2 parents 9758b19 + ad5c61c commit c0d4e3c
Show file tree
Hide file tree
Showing 190 changed files with 2,568 additions and 2,601 deletions.
2 changes: 1 addition & 1 deletion migrations_lockfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ hybridcloud: 0016_add_control_cacheversion
nodestore: 0002_nodestore_no_dictfield
remote_subscriptions: 0003_drop_remote_subscription
replays: 0004_index_together
sentry: 0776_drop_group_score_in_database
sentry: 0777_add_related_name_to_dashboard_permissions
social_auth: 0002_default_auto_field
uptime: 0017_unique_on_timeout
workflow_engine: 0009_detector_type
4 changes: 2 additions & 2 deletions requirements-base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ rfc3986-validator>=0.1.1
sentry-arroyo>=2.16.5
sentry-kafka-schemas>=0.1.111
sentry-ophio==1.0.0
sentry-protos>=0.1.23
sentry-protos>=0.1.26
sentry-redis-tools>=0.1.7
sentry-relay>=0.9.2
sentry-sdk>=2.16.0
sentry-sdk>=2.17.0
slack-sdk>=3.27.2
snuba-sdk>=3.0.43
simplejson>=3.17.6
Expand Down
4 changes: 2 additions & 2 deletions requirements-dev-frozen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,10 @@ sentry-forked-django-stubs==5.1.0.post2
sentry-forked-djangorestframework-stubs==3.15.1.post2
sentry-kafka-schemas==0.1.111
sentry-ophio==1.0.0
sentry-protos==0.1.23
sentry-protos==0.1.26
sentry-redis-tools==0.1.7
sentry-relay==0.9.2
sentry-sdk==2.16.0
sentry-sdk==2.17.0
sentry-usage-accountant==0.0.10
simplejson==3.17.6
six==1.16.0
Expand Down
4 changes: 2 additions & 2 deletions requirements-frozen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ s3transfer==0.10.0
sentry-arroyo==2.16.5
sentry-kafka-schemas==0.1.111
sentry-ophio==1.0.0
sentry-protos==0.1.23
sentry-protos==0.1.26
sentry-redis-tools==0.1.7
sentry-relay==0.9.2
sentry-sdk==2.16.0
sentry-sdk==2.17.0
sentry-usage-accountant==0.0.10
simplejson==3.17.6
six==1.16.0
Expand Down
2 changes: 1 addition & 1 deletion src/flagpole/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ConditionOperatorKind(str, Enum):
"""Provided a single value, check if the property (a list) is not included"""

EQUALS = "equals"
"""Comprare a value to another. Values are compared with types"""
"""Compare a value to another. Values are compared with types"""

NOT_EQUALS = "not_equals"
"""Compare a value to not be equal to another. Values are compared with types"""
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/admin_project_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get(self, request: Request) -> Response:
else:
configs[key] = None

# TODO if we don't think we'll add anything to the endpoint
# TODO: if we don't think we'll add anything to the endpoint
# we may as well return just the configs
return Response({"configs": configs}, status=200)

Expand Down
27 changes: 20 additions & 7 deletions src/sentry/api/endpoints/organization_access_request_details.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from django.db import IntegrityError, router, transaction
from rest_framework import serializers
from rest_framework.request import Request
Expand All @@ -11,8 +13,11 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.serializers import serialize
from sentry.models.organizationaccessrequest import OrganizationAccessRequest
from sentry.models.organizationmember import OrganizationMember
from sentry.models.organizationmemberteam import OrganizationMemberTeam

logger = logging.getLogger(__name__)


class AccessRequestPermission(OrganizationPermission):
scope_map = {
Expand Down Expand Up @@ -71,16 +76,16 @@ def _can_access(self, request: Request, access_request):

def get(self, request: Request, organization) -> Response:
"""
Get list of requests to join org/team
Get a list of requests to join org/team.
If any requests are redundant (user already joined the team), they are not returned.
"""
if request.access.has_scope("org:write"):
access_requests = list(
OrganizationAccessRequest.objects.filter(
team__organization=organization,
member__user_is_active=True,
member__user_id__isnull=False,
).select_related("team")
).select_related("team", "member")
)

elif request.access.has_scope("team:write") and request.access.team_ids_with_membership:
Expand All @@ -89,20 +94,28 @@ def get(self, request: Request, organization) -> Response:
member__user_is_active=True,
member__user_id__isnull=False,
team__id__in=request.access.team_ids_with_membership,
).select_related("team")
).select_related("team", "member")
)
else:
# Return empty response if user does not have access
return Response([])

return Response(serialize(access_requests, request.user))
teams_by_user = OrganizationMember.objects.get_teams_by_user(organization=organization)

# We omit any requests which are now redundant (i.e. the user joined that team some other way)
valid_access_requests = [
access_request
for access_request in access_requests
if access_request.member.user_id is not None
and access_request.team_id not in teams_by_user[access_request.member.user_id]
]

return Response(serialize(valid_access_requests, request.user))

def put(self, request: Request, organization, request_id) -> Response:
"""
Approve or deny a request
Approve or deny a request.
{method} {path}
"""
Expand Down
22 changes: 22 additions & 0 deletions src/sentry/api/endpoints/organization_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
SAFE_FIELDS_DEFAULT,
SCRAPE_JAVASCRIPT_DEFAULT,
SENSITIVE_FIELDS_DEFAULT,
TARGET_SAMPLE_RATE_DEFAULT,
UPTIME_AUTODETECTION,
)
from sentry.datascrubbing import validate_pii_config_update, validate_pii_selectors
Expand Down Expand Up @@ -215,6 +216,7 @@
METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
),
("uptimeAutodetection", "sentry:uptime_autodetection", bool, UPTIME_AUTODETECTION),
("targetSampleRate", "sentry:target_sample_rate", float, TARGET_SAMPLE_RATE_DEFAULT),
)

DELETION_STATUSES = frozenset(
Expand Down Expand Up @@ -276,6 +278,7 @@ class OrganizationSerializer(BaseOrganizationSerializer):
relayPiiConfig = serializers.CharField(required=False, allow_blank=True, allow_null=True)
apdexThreshold = serializers.IntegerField(min_value=1, required=False)
uptimeAutodetection = serializers.BooleanField(required=False)
targetSampleRate = serializers.FloatField(required=False)

@cached_property
def _has_legacy_rate_limits(self):
Expand Down Expand Up @@ -365,6 +368,25 @@ def validate_projectRateLimit(self, value):
)
return value

def validate_targetSampleRate(self, value):
from sentry import features

organization = self.context["organization"]
request = self.context["request"]
has_dynamic_sampling_custom = features.has(
"organizations:dynamic-sampling-custom", organization, actor=request.user
)
if not has_dynamic_sampling_custom:
raise serializers.ValidationError(
"Organization does not have the custom dynamic sample rate feature enabled."
)

if not 0.0 <= value <= 1.0:
raise serializers.ValidationError(
"The targetSampleRate option must be in the range [0:1]"
)
return value

def validate(self, attrs):
attrs = super().validate(attrs)
if attrs.get("avatarType") == "upload":
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/organization_events_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ def build_span_query(trace_id: str, spans_params: SnubaParams, query_spans: list
sentry_sdk.set_measurement("trace_view.spans.span_minimum", span_minimum)
sentry_sdk.set_tag("trace_view.split_by_char.optimization", len(query_spans) > span_minimum)
if len(query_spans) > span_minimum:
# TODO because we're not doing an IN on a list of literals, snuba will not optimize the query with the HexInt
# TODO: because we're not doing an IN on a list of literals, snuba will not optimize the query with the HexInt
# column processor which means we won't be taking advantage of the span_id index but if we only do this when we
# have a lot of query_spans we should have a great performance improvement still once we do that we can simplify
# this code and always apply this optimization
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/organization_events_trends.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class TrendColumns(TypedDict):
TREND_TYPES = [IMPROVED, REGRESSION]


# TODO move this to the builder file and introduce a top-events version instead
# TODO: move this to the builder file and introduce a top-events version instead
class TrendQueryBuilder(DiscoverQueryBuilder):
def convert_aggregate_filter_to_condition(
self, aggregate_filter: AggregateFilter
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/organization_events_trends_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def get_timeseries(top_events, _, rollup, zerofill_results):
results[result_key]["data"].append(row)
else:
discarded += 1
# TODO filter out entries that don't have transaction or trend_function
# TODO: filter out entries that don't have transaction or trend_function
logger.warning(
"trends.top-events.timeseries.key-mismatch",
extra={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def get(self, request: Request, organization: Organization, tag_name: str) -> Re
for project in projects
):
if len(metric_names) == 1 and metric_names[0].startswith("d:eap"):
# TODO hack for EAP, hardcode some metric names
# TODO: hack for EAP, hardcode some metric names
if tag_name == "color":
return Response(
[
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/organization_metrics_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def get(self, request: Request, organization: Organization) -> Response:
for project in projects
):
if metric_name.startswith("d:eap"):
# TODO hack for EAP, return a fixed list
# TODO: hack for EAP, return a fixed list
return Response([Tag(key="color"), Tag(key="location")])

try:
Expand Down
4 changes: 3 additions & 1 deletion src/sentry/api/endpoints/seer_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from rest_framework.response import Response
from sentry_sdk import Scope, capture_exception

from sentry import options
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.authentication import AuthenticationSiloLimit, StandardAuthentication
Expand Down Expand Up @@ -153,8 +154,9 @@ def get_organization_slug(*, org_id: int) -> dict:
def get_organization_autofix_consent(*, org_id: int) -> dict:
org: Organization = Organization.objects.get(id=org_id)
consent = org.get_option("sentry:gen_ai_consent", False)
github_extension_enabled = org_id in options.get("github-extension.enabled-orgs")
return {
"consent": consent,
"consent": consent or github_extension_enabled,
}


Expand Down
4 changes: 2 additions & 2 deletions src/sentry/api/helpers/actionable_items_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class ActionPriority:
EventError.INVALID_ENVIRONMENT: ActionPriority.LOW,
EventError.NATIVE_BAD_DSYM: ActionPriority.LOW,
EventError.NATIVE_MISSING_DSYM: ActionPriority.LOW,
EventError.NATIVE_INTERNAL_FAILURE: ActionPriority.LOW,
EventError.NATIVE_SYMBOLICATOR_FAILED: ActionPriority.LOW,
EventError.NATIVE_MISSING_OPTIONALLY_BUNDLED_DSYM: ActionPriority.LOW,
EventError.PAST_TIMESTAMP: ActionPriority.LOW,
EventError.PROGUARD_MISSING_LINENO: ActionPriority.LOW,
Expand Down Expand Up @@ -66,12 +68,10 @@ class ActionPriority:
EventError.JS_SCRAPING_DISABLED,
EventError.JS_TOO_MANY_REMOTE_SOURCES,
EventError.MISSING_ATTRIBUTE,
EventError.NATIVE_INTERNAL_FAILURE,
EventError.NATIVE_MISSING_SYMBOL,
EventError.NATIVE_MISSING_SYSTEM_DSYM,
EventError.NATIVE_NO_CRASHED_THREAD,
EventError.NATIVE_SIMULATOR_FRAME,
EventError.NATIVE_SYMBOLICATOR_FAILED,
EventError.NATIVE_UNKNOWN_IMAGE,
EventError.UNKNOWN_ERROR,
EventError.VALUE_TOO_LONG,
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/paginator.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ def get_result(self, limit, cursor=None):
prev=Cursor(0, max(0, offset - limit), True, offset > 0),
next=Cursor(0, max(0, offset + limit), False, has_more),
)
# TODO use Cursor.value as the `end` argument to data_fn() so that
# TODO: use Cursor.value as the `end` argument to data_fn() so that
# subsequent pages returned using these cursors are using the same end
# date for queries, this should stop drift from new incoming events.

Expand Down
15 changes: 15 additions & 0 deletions src/sentry/api/serializers/models/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sentry.api.serializers import Serializer, register, serialize
from sentry.constants import ALL_ACCESS_PROJECTS
from sentry.models.dashboard import Dashboard
from sentry.models.dashboard_permissions import DashboardPermissions
from sentry.models.dashboard_widget import (
DashboardWidget,
DashboardWidgetDisplayTypes,
Expand Down Expand Up @@ -64,6 +65,10 @@ class DashboardWidgetResponse(TypedDict):
layout: dict[str, int]


class DashboardPermissionsResponse(TypedDict):
is_creator_only_editable: bool


@register(DashboardWidget)
class DashboardWidgetSerializer(Serializer):
def get_attrs(self, item_list, user, **kwargs):
Expand Down Expand Up @@ -169,6 +174,14 @@ def serialize(self, obj, attrs, user, **kwargs) -> DashboardWidgetQueryResponse:
}


@register(DashboardPermissions)
class DashboardPermissionsSerializer(Serializer):
def serialize(self, obj, attrs, user, **kwargs) -> DashboardPermissionsResponse:
return {
"is_creator_only_editable": obj.is_creator_only_editable,
}


class DashboardListResponse(TypedDict):
id: str
title: str
Expand Down Expand Up @@ -259,6 +272,7 @@ class DashboardDetailsResponse(DashboardDetailsResponseOptional):
widgets: list[DashboardWidgetResponse]
projects: list[int]
filters: DashboardFilters
permissions: DashboardPermissionsResponse | None


@register(Dashboard)
Expand Down Expand Up @@ -294,6 +308,7 @@ def serialize(self, obj, attrs, user, **kwargs) -> DashboardDetailsResponse:
"widgets": attrs["widgets"],
"projects": [project.id for project in obj.projects.all()],
"filters": {},
"permissions": serialize(obj.permissions) if hasattr(obj, "permissions") else None,
}

if obj.filters is not None:
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/serializers/models/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ def serialize(


class _DetailedOrganizationSerializerResponseOptional(OrganizationSerializerResponse, total=False):
role: Any # TODO replace with enum/literal
role: Any # TODO: replace with enum/literal
orgRole: str
uptimeAutodetection: bool

Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/serializers/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ class ProjectSerializerResponse(ProjectSerializerBaseResponse):
isPublic: bool
avatar: SerializedAvatarFields
color: str
status: str # TODO enum/literal
status: str # TODO: enum/literal


@register(Project)
Expand Down
11 changes: 11 additions & 0 deletions src/sentry/api/serializers/rest_framework/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,12 @@ def validate(self, data):
return data


class DashboardPermissionsSerializer(CamelSnakeSerializer[Dashboard]):
is_creator_only_editable = serializers.BooleanField(
help_text="Whether the dashboard is editable only by the creator.",
)


class DashboardDetailsSerializer(CamelSnakeSerializer[Dashboard]):
# Is a string because output serializers also make it a string.
id = serializers.CharField(required=False, help_text="A dashboard's unique id.")
Expand Down Expand Up @@ -494,6 +500,11 @@ class DashboardDetailsSerializer(CamelSnakeSerializer[Dashboard]):
help_text="Setting that lets you display saved time range for this dashboard in UTC.",
)
validate_id = validate_id
permissions = DashboardPermissionsSerializer(
required=False,
allow_null=True,
help_text="Permissions that restrict users from editing dashboards",
)

def validate_projects(self, projects):
from sentry.api.validators import validate_project_ids
Expand Down
1 change: 1 addition & 0 deletions src/sentry/apidocs/examples/dashboard_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"projects": [1],
"filters": {},
"period": "7d",
"permissions": {"is_creator_only_editable": False},
}

DASHBOARDS_OBJECT = [
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/buffer/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# load everywhere
_last_validation_log: float | None = None
Pipeline = Any
# TODO type Pipeline instead of using Any here
# TODO: type Pipeline instead of using Any here


def _get_model_key(model: type[models.Model]) -> str:
Expand Down
1 change: 1 addition & 0 deletions src/sentry/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ class InsightModules(Enum):
METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT = False
DATA_CONSENT_DEFAULT = False
UPTIME_AUTODETECTION = True
TARGET_SAMPLE_RATE_DEFAULT = 1.0

# `sentry:events_member_admin` - controls whether the 'member' role gets the event:admin scope
EVENTS_MEMBER_ADMIN_DEFAULT = True
Expand Down
Loading

0 comments on commit c0d4e3c

Please sign in to comment.