Skip to content

Commit

Permalink
Merge branch 'master' into chore-cleanup-query-audits
Browse files Browse the repository at this point in the history
  • Loading branch information
markstory authored Jul 13, 2023
2 parents a96e10e + df32ba7 commit 6ea7521
Show file tree
Hide file tree
Showing 80 changed files with 2,036 additions and 558 deletions.
9 changes: 9 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@
/src/sentry/api/authentication.py @getsentry/security @getsentry/enterprise
/src/sentry/api/endpoints/auth* @getsentry/security @getsentry/enterprise
/src/sentry/api/endpoints/user_permission* @getsentry/security @getsentry/enterprise
/src/sentry/web/frontend/auth_close.py @getsentry/security
/src/sentry/web/frontend/auth_login.py @getsentry/security
/src/sentry/web/frontend/auth_logout.py @getsentry/security
/src/sentry/web/frontend/auth_organization_id_login.py @getsentry/security
/src/sentry/web/frontend/auth_organization_login.py @getsentry/security
/src/sentry/web/frontend/auth_provider_login.py @getsentry/security
/src/sentry/web/frontend/oauth_token.py @getsentry/security
/src/sentry/web/frontend/oauth_authorize.py @getsentry/security
/src/sentry/web/frontend/openidtoken.py @getsentry/security

## Dev
/.github/ @getsentry/owners-sentry-dev
Expand Down
4 changes: 2 additions & 2 deletions requirements-base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ datadog>=0.29.3
django-crispy-forms>=1.14.0
django-csp>=3.7
django-pg-zero-downtime-migrations>=0.11
Django>=2.2.28
Django>=3.2.20,<4
djangorestframework>=3.12.4
drf-spectacular>=0.22.1
email-reply-parser>=0.5.12
Expand Down Expand Up @@ -62,7 +62,7 @@ sentry-arroyo>=2.14.1
sentry-kafka-schemas>=0.1.16
sentry-redis-tools>=0.1.7
sentry-relay>=0.8.28
sentry-sdk>=1.28.0
sentry-sdk>=1.28.1
snuba-sdk>=1.0.5
simplejson>=3.17.6
sqlparse>=0.4.4
Expand Down
5 changes: 3 additions & 2 deletions requirements-dev-frozen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
aiohttp==3.8.4
aiosignal==1.3.1
amqp==2.6.1
asgiref==3.7.2
async-generator==1.10
async-timeout==4.0.2
attrs==19.2.0
Expand Down Expand Up @@ -38,7 +39,7 @@ datadog==0.29.3
decorator==5.1.1
dictpath==0.1.3
distlib==0.3.4
django==2.2.28
django==3.2.20
django-crispy-forms==1.14.0
django-csp==3.7
django-pg-zero-downtime-migrations==0.11
Expand Down Expand Up @@ -172,7 +173,7 @@ sentry-cli==2.16.0
sentry-kafka-schemas==0.1.16
sentry-redis-tools==0.1.7
sentry-relay==0.8.28
sentry-sdk==1.28.0
sentry-sdk==1.28.1
simplejson==3.17.6
six==1.16.0
sniffio==1.2.0
Expand Down
5 changes: 3 additions & 2 deletions requirements-frozen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
aiohttp==3.8.4
aiosignal==1.3.1
amqp==2.6.1
asgiref==3.7.2
async-generator==1.10
async-timeout==4.0.2
attrs==19.2.0
Expand All @@ -31,7 +32,7 @@ cssselect==1.0.3
cssutils==2.4.0
datadog==0.29.3
decorator==5.1.1
django==2.2.28
django==3.2.20
django-crispy-forms==1.14.0
django-csp==3.7
django-pg-zero-downtime-migrations==0.11
Expand Down Expand Up @@ -119,7 +120,7 @@ sentry-arroyo==2.14.1
sentry-kafka-schemas==0.1.16
sentry-redis-tools==0.1.7
sentry-relay==0.8.28
sentry-sdk==1.28.0
sentry-sdk==1.28.1
simplejson==3.17.6
six==1.16.0
sniffio==1.2.0
Expand Down
93 changes: 56 additions & 37 deletions src/sentry/api/endpoints/organization_profiling_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
from sentry.api.paginator import GenericOffsetPaginator
from sentry.exceptions import InvalidSearchQuery
from sentry.net.http import connection_from_url
from sentry.search.events.builder import ProfileTopFunctionsTimeseriesQueryBuilder
from sentry.snuba import functions
from sentry.snuba.dataset import Dataset
from sentry.snuba.referrer import Referrer
from sentry.utils import json
from sentry.utils.dates import parse_stats_period, validate_interval
from sentry.utils.sdk import set_measurement
from sentry.utils.snuba import bulk_snql_query

ads_connection_pool = connection_from_url(
settings.ANOMALY_DETECTION_URL,
Expand Down Expand Up @@ -90,49 +93,65 @@ def get(self, request: Request, organization) -> Response:
return Response(serializer.errors, status=400)
data = serializer.validated_data

top_functions = {}

def get_event_stats(columns, query, params, _rollup, zerofill_results, comparison_delta):
nonlocal top_functions
top_functions = functions.query(
selected_columns=[
"project.id",
"fingerprint",
"package",
"function",
"count()",
"examples()",
],
query=data.get("query"),
params=params,
orderby=["-count()"],
limit=TOP_FUNCTIONS_LIMIT,
referrer=Referrer.API_PROFILING_FUNCTION_TRENDS_TOP_EVENTS.value,
auto_aggregations=True,
use_aggregate_conditions=True,
transform_alias_to_input_format=True,
)

def get_event_stats(columns, query, params, _rollup, zerofill_results, _comparison_delta):
rollup = get_rollup_from_range(params["end"] - params["start"])

top_functions = functions.query(
selected_columns=[
"project.id",
"fingerprint",
"package",
"function",
"count()",
"examples()",
],
query=query,
params=params,
orderby=["-count()"],
limit=TOP_FUNCTIONS_LIMIT,
referrer=Referrer.API_PROFILING_FUNCTION_TRENDS_TOP_EVENTS.value,
auto_aggregations=True,
use_aggregate_conditions=True,
transform_alias_to_input_format=True,
)
chunks = [
top_functions["data"][i : i + FUNCTIONS_PER_QUERY]
for i in range(0, len(top_functions["data"]), FUNCTIONS_PER_QUERY)
]

results = functions.top_events_timeseries(
timeseries_columns=columns,
selected_columns=["project.id", "fingerprint"],
query=query,
params=params,
orderby=None,
rollup=rollup,
limit=TOP_FUNCTIONS_LIMIT,
top_events=top_functions,
organization=organization,
zerofill_results=zerofill_results,
referrer=Referrer.API_PROFILING_FUNCTION_TRENDS_STATS.value,
# this ensures the result key is formatted as `{project.id},{fingerprint}`
# in order to be compatible with the trends service
result_key_order=["project.id", "fingerprint"],
builders = [
ProfileTopFunctionsTimeseriesQueryBuilder(
dataset=Dataset.Functions,
params=params,
interval=rollup,
top_events=chunk,
other=False,
query=query,
selected_columns=["project.id", "fingerprint"],
timeseries_columns=columns,
skip_tag_resolution=True,
)
for chunk in chunks
]
bulk_results = bulk_snql_query(
[builder.get_snql_query() for builder in builders],
Referrer.API_PROFILING_FUNCTION_TRENDS_STATS.value,
)

results = {}

for chunk, builder, result in zip(chunks, builders, bulk_results):
formatted_results = functions.format_top_events_timeseries_results(
result,
builder,
params,
rollup,
top_events={"data": chunk},
result_key_order=["project.id", "fingerprint"],
)
results.update(formatted_results)

return results

def get_trends_data(stats_data):
Expand Down
22 changes: 12 additions & 10 deletions src/sentry/db/router.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import logging
import sys
from typing import Iterable, Optional
Expand Down Expand Up @@ -79,7 +81,7 @@ def use_simulated(self, value: bool):
raise ValueError("Cannot mutate simulation mode outside of tests")
self.__is_simulated = value

def _resolve_silo_connection(self, silo_modes: Iterable[SiloMode], table: str) -> str:
def _resolve_silo_connection(self, silo_modes: Iterable[SiloMode], table: str) -> str | None:
# XXX This method has an override in getsentry for region silo primary splits.
active_mode = SiloMode.get_current_mode()

Expand All @@ -93,15 +95,15 @@ def _resolve_silo_connection(self, silo_modes: Iterable[SiloMode], table: str) -
if active_mode == silo_mode:
return "default"

# If we're in tests raise an error, otherwise return 'no decision'
# so that django skips migration operations that won't work.
if "pytest" in sys.argv[0]:
raise SiloConnectionUnavailableError(
f"Cannot resolve table {table} in {silo_mode}. "
f"Application silo mode is {active_mode} and simulated silos are not enabled."
)
else:
return None
# If we're in tests raise an error, otherwise return 'no decision'
# so that django skips migration operations that won't work.
if "pytest" in sys.argv[0]:
raise SiloConnectionUnavailableError(
f"Cannot resolve table {table} in {silo_modes}. "
f"Application silo mode is {active_mode} and simulated silos are not enabled."
)
else:
return None

def _find_model(self, table: str, app_label: str) -> Optional[Model]:
# Use django's model inventory to find our table and what silo it is on.
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/integrations/jira_server/urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from django.urls import re_path

from .search import JiraServerSearchEndpoint
from .webhooks import JiraIssueUpdatedWebhook
from .webhooks import JiraServerIssueUpdatedWebhook

# If updating/adding URLs here, make sure to update the JiraServerRequestParser as well
urlpatterns = [
re_path(
r"^issue-updated/(?P<token>[^\/]+)/$",
JiraIssueUpdatedWebhook.as_view(),
JiraServerIssueUpdatedWebhook.as_view(),
name="sentry-extensions-jiraserver-issue-updated",
),
re_path(
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/integrations/jira_server/webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def get_integration_from_token(token):


@control_silo_endpoint
class JiraIssueUpdatedWebhook(Endpoint):
class JiraServerIssueUpdatedWebhook(Endpoint):
authentication_classes = ()
permission_classes = ()

Expand Down
2 changes: 2 additions & 0 deletions src/sentry/middleware/integrations/integration_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
GithubRequestParser,
GitlabRequestParser,
JiraRequestParser,
JiraServerRequestParser,
MsTeamsRequestParser,
SlackRequestParser,
VstsRequestParser,
Expand All @@ -23,6 +24,7 @@
GithubRequestParser,
GitlabRequestParser,
JiraRequestParser,
JiraServerRequestParser,
SlackRequestParser,
MsTeamsRequestParser,
BitbucketRequestParser,
Expand Down
2 changes: 2 additions & 0 deletions src/sentry/middleware/integrations/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .github import GithubRequestParser
from .gitlab import GitlabRequestParser
from .jira import JiraRequestParser
from .jira_server import JiraServerRequestParser
from .msteams import MsTeamsRequestParser
from .slack import SlackRequestParser
from .vsts import VstsRequestParser
Expand All @@ -11,6 +12,7 @@
"GithubRequestParser",
"GitlabRequestParser",
"JiraRequestParser",
"JiraServerRequestParser",
"MsTeamsRequestParser",
"SlackRequestParser",
"VstsRequestParser",
Expand Down
56 changes: 56 additions & 0 deletions src/sentry/middleware/integrations/parsers/jira_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from __future__ import annotations

import logging

from sentry.integrations.jira_server.search import JiraServerSearchEndpoint
from sentry.integrations.jira_server.webhooks import (
JiraServerIssueUpdatedWebhook,
get_integration_from_token,
)
from sentry.middleware.integrations.parsers.base import BaseRequestParser
from sentry.models.organizationmapping import OrganizationMapping
from sentry.models.outbox import WebhookProviderIdentifier
from sentry.types.region import RegionResolutionError, get_region_by_name

logger = logging.getLogger(__name__)


class JiraServerRequestParser(BaseRequestParser):
provider = "jira_server"
webhook_identifier = WebhookProviderIdentifier.JIRA_SERVER

def get_response_from_search_endpoint(self):
organization_slug = self.match.kwargs.get("organization_slug")
mapping: OrganizationMapping = OrganizationMapping.objects.filter(
slug=organization_slug
).first()
if not mapping:
logger.error("no_mapping", extra={"slug": organization_slug})
return self.get_response_from_control_silo()
try:
region = get_region_by_name(name=mapping.region_name)
except RegionResolutionError:
logger.error(
"no_region",
extra={"slug": organization_slug, "mapping_region": mapping.region_name},
)
return self.get_response_from_control_silo()
return self.get_response_from_region_silo(region=region)

def get_response_from_issue_update_webhook(self):
token = self.match.kwargs.get("token")
try:
integration = get_integration_from_token(token)
except ValueError as e:
logger.error("no_integration", extra={"error": str(e)})
return self.get_response_from_control_silo()
organizations = self.get_organizations_from_integration(integration=integration)
regions = self.get_regions_from_organizations(organizations=organizations)
return self.get_response_from_outbox_creation(regions=regions)

def get_response(self):
if self.match.func.view_class == JiraServerIssueUpdatedWebhook: # type: ignore
return self.get_response_from_issue_update_webhook()
elif self.match.func.view_class == JiraServerSearchEndpoint: # type: ignore
return self.get_response_from_search_endpoint()
return self.get_response_from_control_silo()
1 change: 1 addition & 0 deletions src/sentry/models/outbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class WebhookProviderIdentifier(IntEnum):
MSTEAMS = 4
BITBUCKET = 5
VSTS = 6
JIRA_SERVER = 7


def _ensure_not_null(k: str, v: Any) -> Any:
Expand Down
10 changes: 5 additions & 5 deletions src/sentry/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ def get_users_with_only_one_integration_for_provider(
For a given organization, get the list of members that are only
connected to a single integration.
"""
from sentry.models import OrganizationMember
from sentry.models import OrganizationMemberMapping
from sentry.models.integrations.organization_integration import OrganizationIntegration

org_user_ids = OrganizationMember.objects.filter(organization_id=organization_id).values(
"user_id"
)
org_user_ids = OrganizationMemberMapping.objects.filter(
organization_id=organization_id
).values("user_id")
org_members_with_provider = (
OrganizationMember.objects.values("user_id")
OrganizationMemberMapping.objects.values("user_id")
.annotate(org_counts=Count("organization_id"))
.filter(
user_id__in=Subquery(org_user_ids),
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/new_migrations/monkey/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from sentry.new_migrations.monkey.executor import SentryMigrationExecutor
from sentry.new_migrations.monkey.fields import deconstruct

LAST_VERIFIED_DJANGO_VERSION = (2, 2)
LAST_VERIFIED_DJANGO_VERSION = (3, 2)
CHECK_MESSAGE = """Looks like you're trying to upgrade Django! Since we monkeypatch
the Django migration library in several places, please verify that we have the latest
code, and that the monkeypatching still works as expected. Currently the main things
Expand Down
Loading

0 comments on commit 6ea7521

Please sign in to comment.