Skip to content

Commit

Permalink
Merge branch 'master' into gv/pydantic-v2-7-upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
GabeVillalobos authored Jul 30, 2024
2 parents 2b75824 + ca1bcbe commit 37d54bb
Show file tree
Hide file tree
Showing 96 changed files with 1,749 additions and 640 deletions.
7 changes: 3 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestPath": "${workspaceFolder}/.venv/bin/pytest",
"python.testing.pytestArgs": [
"tests"
],
"python.analysis.autoImportCompletions": true
"python.testing.pytestArgs": ["tests"],
"python.analysis.autoImportCompletions": true,
"prettier.configPath": "package.json"
}
2 changes: 1 addition & 1 deletion src/sentry/api/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ def accepts_auth(self, auth: list[bytes]) -> bool:
return not token_str.startswith(SENTRY_ORG_AUTH_TOKEN_PREFIX)

def authenticate_token(self, request: Request, token_str: str) -> tuple[Any, Any]:
user: AnonymousUser | RpcUser | None = AnonymousUser()
user: AnonymousUser | User | RpcUser | None = AnonymousUser()

token: SystemToken | ApiTokenReplica | ApiToken | None = SystemToken.from_request(
request, token_str
Expand Down
14 changes: 13 additions & 1 deletion src/sentry/api/endpoints/artifact_lookup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from __future__ import annotations

import logging
from collections.abc import Iterable
from typing import NotRequired, TypedDict

from django.db.models.query import QuerySet
from django.http import Http404, HttpResponse, StreamingHttpResponse
Expand Down Expand Up @@ -36,6 +39,15 @@
MAX_RELEASEFILES_QUERY = 10


class _Artifact(TypedDict):
id: str
type: str
url: str
resolved_with: str
abs_path: NotRequired[str]
headers: NotRequired[dict[str, object]]


@region_silo_endpoint
class ProjectArtifactLookupEndpoint(ProjectEndpoint):
owner = ApiOwner.PROCESSING
Expand Down Expand Up @@ -155,7 +167,7 @@ def get(self, request: Request, project: Project) -> Response:
# Then: Construct our response
url_constructor = UrlConstructor(request, project)

found_artifacts = []
found_artifacts: list[_Artifact] = []
for download_id, resolved_with in all_bundles.items():
found_artifacts.append(
{
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/oauth_userinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def get(self, request: Request) -> Response:
raise InsufficientScopesError

user = token_details.user
user_output = {"sub": user.id}
user_output: dict[str, object] = {"sub": user.id}
if "profile" in scopes:
profile_details = {
"name": user.name,
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/api/endpoints/organization_events_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def get(self, request: Request, organization) -> Response:
return Response(status=404)

try:
params = self.get_snuba_params(request, organization)
snuba_params, _ = self.get_snuba_dataclass(request, organization)
except NoProjects:
return Response({})

Expand All @@ -81,7 +81,8 @@ def get(self, request: Request, organization) -> Response:
results = dataset.histogram_query(
fields=data["field"],
user_query=data.get("query"),
params=params,
params={},
snuba_params=snuba_params,
num_buckets=data["numBuckets"],
precision=data["precision"],
min_value=data.get("min"),
Expand Down
8 changes: 4 additions & 4 deletions src/sentry/api/endpoints/organization_measurements_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ class OrganizationMeasurementsMeta(OrganizationEventsEndpointBase):

def get(self, request: Request, organization: Organization) -> Response:
try:
params = self.get_snuba_params(request, organization)
snuba_params, _ = self.get_snuba_dataclass(request, organization)
except NoProjects:
return Response({})

with handle_query_errors():
metric_meta = get_custom_measurements(
project_ids=params["project_id"],
project_ids=snuba_params.project_ids,
organization_id=organization.id,
start=params["start"],
end=params["end"],
start=snuba_params.start_date,
end=snuba_params.end_date,
use_case_id=UseCaseID.TRANSACTIONS,
)

Expand Down
27 changes: 14 additions & 13 deletions src/sentry/api/endpoints/organization_profiling_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
)
from sentry.profiles.profile_chunks import get_chunk_ids
from sentry.profiles.utils import proxy_profiling_service
from sentry.snuba.dataset import Dataset


class OrganizationProfilingBaseEndpoint(OrganizationEventsV2EndpointBase):
Expand All @@ -34,25 +33,27 @@ class OrganizationProfilingBaseEndpoint(OrganizationEventsV2EndpointBase):
class OrganizationProfilingFlamegraphSerializer(serializers.Serializer):
# fingerprint is an UInt32
fingerprint = serializers.IntegerField(min_value=0, max_value=(1 << 32) - 1, required=False)
dataset = serializers.ChoiceField(["profiles", "discover", "functions"], required=False)
dataSource = serializers.ChoiceField(["transactions", "profiles", "functions"], required=False)
query = serializers.CharField(required=False)

def validate(self, attrs):
dataset = attrs.get("dataset")
source = attrs.get("dataSource")

if dataset is None:
if source is None:
if attrs.get("fingerprint") is not None:
attrs["dataset"] = Dataset.Functions
attrs["dataSource"] = "functions"
else:
attrs["dataset"] = Dataset.Discover
elif dataset == "functions":
attrs["dataset"] = Dataset.Functions
attrs["dataSource"] = "transactions"
elif source == "functions":
attrs["dataSource"] = "functions"
elif attrs.get("fingerprint") is not None:
raise ParseError(
detail='"fingerprint" is only permitted when using dataset: "functions"'
detail='"fingerprint" is only permitted when using dataSource: "functions"'
)
elif source == "profiles":
attrs["dataSource"] = "profiles"
else:
attrs["dataset"] = Dataset.Discover
attrs["dataSource"] = "transactions"

return attrs

Expand All @@ -73,7 +74,7 @@ def get(self, request: Request, organization: Organization) -> HttpResponse:
raise ParseError(detail="You cannot get a flamegraph from multiple projects.")

if request.query_params.get("fingerprint"):
sentry_sdk.set_tag("dataset", "functions")
sentry_sdk.set_tag("data source", "functions")
function_fingerprint = int(request.query_params["fingerprint"])

profile_ids = get_profiles_with_function(
Expand All @@ -84,7 +85,7 @@ def get(self, request: Request, organization: Organization) -> HttpResponse:
request.GET.get("query", ""),
)
else:
sentry_sdk.set_tag("dataset", "profiles")
sentry_sdk.set_tag("data source", "profiles")
profile_ids = get_profile_ids(snuba_params, request.query_params.get("query", None))

return proxy_profiling_service(
Expand All @@ -106,7 +107,7 @@ def get(self, request: Request, organization: Organization) -> HttpResponse:
with handle_query_errors():
executor = FlamegraphExecutor(
snuba_params=snuba_params,
dataset=serialized["dataset"],
data_source=serialized["dataSource"],
query=serialized.get("query", ""),
fingerprint=serialized.get("fingerprint"),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def get(self, request: Request, organization) -> Response:

start, end = get_date_range_from_params(request.GET)
time_params = get_time_params(start, end)
query_params = self.get_snuba_params(request, organization)
snuba_params, _ = self.get_snuba_dataclass(request, organization)
query = request.GET.get("query")
query = f"{query} event.type:transaction" if query else "event.type:transaction"

Expand All @@ -126,14 +126,15 @@ def get(self, request: Request, organization) -> Response:
}

# overwrite relevant time params
query_params["start"] = time_params.query_start
query_params["end"] = time_params.query_end
snuba_params.start = time_params.query_start
snuba_params.end = time_params.query_end

with handle_query_errors():
snuba_response = timeseries_query(
selected_columns=["count()"],
query=query,
params=query_params,
params={},
snuba_params=snuba_params,
rollup=time_params.granularity,
referrer="transaction-anomaly-detection",
zerofill_results=False,
Expand Down
1 change: 1 addition & 0 deletions src/sentry/api/endpoints/project_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def post(self, request: Request, project) -> Response:
organization = project.organization
transaction_id = uuid4().hex
url_data = sign(
salt=SALT,
actor_id=request.user.id,
from_organization_id=organization.id,
project_id=project.id,
Expand Down
10 changes: 5 additions & 5 deletions src/sentry/features/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@ class FeatureHandlerStrategy(Enum):
"""

INTERNAL = 1
"""Handle the feature using a constant or logic within python"""
REMOTE = 2
"""Handle the feature using a remote flag management service"""
"""Handle the feature using a logic within a FeatureHandler subclass"""
FLAGPOLE = 2
"""Handle the feature using Flagpole and option backed rules based features.
Features will automatically have options registered for them.
"""
OPTIONS = 3
"""Handle the feature using options. see https://develop.sentry.dev/feature-flags/#building-your-options-based-feature
for more information.
"""
FLAGPOLE = 4
"""Handle the feature using the Flagpole management service"""
17 changes: 7 additions & 10 deletions src/sentry/features/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def add(
"""
entity_feature_strategy = self._shim_feature_strategy(entity_feature_strategy)

if entity_feature_strategy == FeatureHandlerStrategy.REMOTE:
if entity_feature_strategy == FeatureHandlerStrategy.FLAGPOLE:
if name.startswith("users:"):
raise NotImplementedError("User flags not allowed with entity_feature=True")
self.entity_features.add(name)
Expand All @@ -192,15 +192,12 @@ def add(
)
self.option_features.add(name)

is_external_flag = (
# Register all flagpole features with options automator,
# so long as they haven't already been registered.
if (
entity_feature_strategy == FeatureHandlerStrategy.FLAGPOLE
or entity_feature_strategy == FeatureHandlerStrategy.REMOTE
)

# Register all remote and flagpole features with options automator,
# so long as they haven't already been registered. This will allow
# us to backfill and cut over to Flagpole without interruptions.
if is_external_flag and name not in self.flagpole_features:
and name not in self.flagpole_features
):
self.flagpole_features.add(name)
# Set a default of {} to ensure the feature evaluates to None when checked
feature_option_name = f"{FLAGPOLE_OPTION_PREFIX}.{name}"
Expand Down Expand Up @@ -370,7 +367,7 @@ def _shim_feature_strategy(
Shim layer for old API to register a feature until all the features have been converted
"""
if entity_feature_strategy is True:
return FeatureHandlerStrategy.REMOTE
return FeatureHandlerStrategy.FLAGPOLE
elif entity_feature_strategy is False:
return FeatureHandlerStrategy.INTERNAL
return entity_feature_strategy
Expand Down
Loading

0 comments on commit 37d54bb

Please sign in to comment.