Skip to content

Commit ec0ba8f

Browse files
committed
Merge branch 'master' into scttcper/external-issue-tabs
2 parents 0b1ac4e + 864087e commit ec0ba8f

16 files changed

Lines changed: 518 additions & 25 deletions

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ repos:
118118
# "Expected at least one target file" when its config drops every
119119
# input file, so skip them at the hook level instead.
120120
exclude: ^(api-docs|fixtures)/.*\.json$
121-
entry: ./node_modules/.bin/oxfmt
121+
entry: ./node_modules/.bin/oxfmt --no-error-on-unmatched-pattern
122122

123123
- id: knip
124124
name: knip

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/sentry/api/serializers/rest_framework/dashboard.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import datetime, timedelta
44
from enum import Enum
55
from math import floor
6-
from typing import TypedDict
6+
from typing import Any, TypedDict
77

88
import sentry_sdk
99
from django.db.models import Max
@@ -522,6 +522,25 @@ def _validate_text_widget(self, data):
522522

523523
return data
524524

525+
def _validate_tracemetrics_equation_constraints(self, data) -> dict[str, Any]:
526+
if not data.get("widget_type") == DashboardWidgetTypes.TRACEMETRICS:
527+
return data
528+
529+
# Tracemetrics timeseries widgets only support a single equation per query
530+
if data.get("display_type") in {
531+
DashboardWidgetDisplayTypes.LINE_CHART,
532+
DashboardWidgetDisplayTypes.AREA_CHART,
533+
DashboardWidgetDisplayTypes.BAR_CHART,
534+
}:
535+
for query in data.get("queries"):
536+
aggregates = query.get("aggregates") or []
537+
if any(is_equation(aggregate) for aggregate in aggregates) and len(aggregates) > 1:
538+
raise serializers.ValidationError(
539+
{"queries": "Tracemetrics timeseries widgets support at most one equation."}
540+
)
541+
542+
return data
543+
525544
def validate(self, data):
526545
self.query_warnings = {"queries": [], "columns": {}}
527546

@@ -570,6 +589,9 @@ def validate(self, data):
570589
)
571590

572591
if data.get("queries"):
592+
if data.get("widget_type") == DashboardWidgetTypes.TRACEMETRICS:
593+
self._validate_tracemetrics_equation_constraints(data)
594+
573595
# Check each query to see if they have an issue or discover error depending on the type of the widget
574596
for query in data.get("queries"):
575597
if len(query.get("columns", [])) > 0:

src/sentry/event_preprocessors.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Mapping
4+
from typing import Any
5+
6+
from sentry.lang.dart.utils import (
7+
get_debug_meta_image_ids,
8+
has_native_frames_in_stacktraces,
9+
)
10+
from sentry.lang.java.utils import has_proguard_file
11+
from sentry.plugins.base.v2 import EventPreprocessor
12+
13+
14+
def get_event_preprocessors(data: Mapping[str, Any]) -> list[EventPreprocessor]:
15+
"""Return all preprocessors needed for this event."""
16+
from sentry.lang.dart.utils import deobfuscate_exception_type
17+
from sentry.lang.java.processing import deobfuscate_exception_value
18+
from sentry.lang.javascript.preprocessing import preprocess_event
19+
20+
preprocessors: list[EventPreprocessor] = []
21+
if has_proguard_file(data):
22+
preprocessors.append(deobfuscate_exception_value)
23+
if data.get("platform") in ("javascript", "node"):
24+
preprocessors.append(preprocess_event)
25+
if _is_obfuscated_dart_event(data):
26+
preprocessors.append(deobfuscate_exception_type)
27+
return preprocessors
28+
29+
30+
def _is_obfuscated_dart_event(data: Mapping[str, Any]) -> bool:
31+
sdk_name = (data.get("sdk") or {}).get("name", "")
32+
if sdk_name not in ("sentry.dart", "sentry.dart.flutter"):
33+
return False
34+
if not get_debug_meta_image_ids(dict(data)):
35+
return False
36+
return has_native_frames_in_stacktraces(data)

src/sentry/features/temporary.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def register_temporary_features(manager: FeatureManager) -> None:
8686
# Enable default anomaly detection metric monitor for new projects
8787
manager.add("organizations:default-anomaly-detector", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=False)
8888
manager.add("organizations:derive-tags-without-plugins", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
89+
manager.add("organizations:event-preprocessors-without-plugins", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=False)
8990
# Enable the discover saved queries deprecation warnings
9091
manager.add("organizations:discover-saved-queries-deprecation", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
9192
# Enable migration of transaction widgets and queries to spans

src/sentry/lang/dart/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from sentry.stacktraces.processing import find_stacktraces_in_data
99

1010

11+
# Deprecated: preprocessor logic moved to sentry.event_preprocessors.
12+
# TODO(christinarlong): Delete after organizations:event-preprocessors-without-plugins is fully rolled out.
1113
class DartPlugin(Plugin2):
1214
"""
1315
This plugin is responsible for Dart specific processing on events or attachments.

src/sentry/lang/dart/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from sentry.models.debugfile import ProjectDebugFile
1212
from sentry.models.project import Project
13+
from sentry.stacktraces.processing import find_stacktraces_in_data
1314
from sentry.utils.safe import get_path
1415

1516
# Obfuscated type values are either in the form of "xyz" or "xyz<abc>" where
@@ -109,3 +110,11 @@ def replace_symbol(match: re.Match[str]) -> str:
109110
new_value = re.sub(INSTANCE_OF_VALUE_RE, replace_symbol, exception_value)
110111
if new_value != exception_value:
111112
exception["value"] = new_value
113+
114+
115+
def has_native_frames_in_stacktraces(data) -> bool:
116+
for stacktrace_info in find_stacktraces_in_data(data):
117+
frames = stacktrace_info.get_frames()
118+
if frames and any(frame.get("platform") == "native" for frame in frames):
119+
return True
120+
return False

src/sentry/lang/java/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from sentry.plugins.base.v2 import EventPreprocessor, Plugin2
99

1010

11+
# Deprecated: preprocessor logic moved to sentry.event_preprocessors.
12+
# TODO(christinarlong): Delete after organizations:event-preprocessors-without-plugins is fully rolled out.
1113
class JavaPlugin(Plugin2):
1214
can_disable = False
1315

src/sentry/lang/javascript/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def generate_modules(data):
3333
frame["module"] = generate_module(abs_path)
3434

3535

36+
# Deprecated: preprocessor logic moved to sentry.event_preprocessors and sentry.lang.javascript.preprocessing.
37+
# TODO(christinarlong): Delete after organizations:event-preprocessors-without-plugins is fully rolled out.
3638
class JavascriptPlugin(Plugin2):
3739
can_disable = False
3840

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from __future__ import annotations
2+
3+
from sentry.stacktraces.processing import find_stacktraces_in_data
4+
from sentry.utils.safe import get_path
5+
6+
from .errorlocale import translate_exception
7+
from .errormapping import rewrite_exception
8+
from .utils import generate_module
9+
10+
11+
def preprocess_event(data):
12+
rewrite_exception(data)
13+
translate_exception(data)
14+
generate_modules(data)
15+
return data
16+
17+
18+
def generate_modules(data):
19+
for info in find_stacktraces_in_data(data):
20+
for frame in get_path(info.stacktrace, "frames", filter=True, default=()):
21+
platform = frame.get("platform") or data["platform"]
22+
if platform not in ("javascript", "node") or frame.get("module"):
23+
continue
24+
abs_path = frame.get("abs_path")
25+
if abs_path and abs_path.startswith(("http:", "https:", "webpack:", "app:")):
26+
frame["module"] = generate_module(abs_path)

0 commit comments

Comments
 (0)