Skip to content

Commit a6a1a91

Browse files
authored
Merge branch 'master' into aci/nm/monitor-settings
2 parents 3ebac24 + f9b3886 commit a6a1a91

File tree

37 files changed

+469
-382
lines changed

37 files changed

+469
-382
lines changed

migrations_lockfile.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ remote_subscriptions: 0003_drop_remote_subscription
2121

2222
replays: 0005_drop_replay_index
2323

24-
sentry: 0895_relocation_provenance_smallint
24+
sentry: 0896_org_level_access_not_null
2525

2626
social_auth: 0002_default_auto_field
2727

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Generated by Django 5.2.1 on 2025-05-14 18:49
2+
3+
from sentry.new_migrations.migrations import CheckedMigration
4+
from sentry.new_migrations.monkey.special import SafeRunSQL
5+
6+
7+
class Migration(CheckedMigration):
8+
# This flag is used to mark that a migration shouldn't be automatically run in production.
9+
# This should only be used for operations where it's safe to run the migration after your
10+
# code has deployed. So this should not be used for most operations that alter the schema
11+
# of a table.
12+
# Here are some things that make sense to mark as post deployment:
13+
# - Large data migrations. Typically we want these to be run manually so that they can be
14+
# monitored and not block the deploy for a long period of time while they run.
15+
# - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
16+
# run this outside deployments so that we don't block them. Note that while adding an index
17+
# is a schema change, it's completely safe to run the operation after the code has deployed.
18+
# Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment
19+
20+
is_post_deployment = False
21+
22+
dependencies = [
23+
("sentry", "0895_relocation_provenance_smallint"),
24+
]
25+
26+
operations = [
27+
SafeRunSQL(
28+
"""
29+
ALTER TABLE sentry_apiapplication
30+
ALTER COLUMN requires_org_level_access SET NOT NULL
31+
""",
32+
reverse_sql="",
33+
hints={"tables": ["sentry_apiapplication"]},
34+
),
35+
]

src/sentry/models/eventattachment.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,7 @@ def get_crashreport_key(group_id: int) -> str:
3434
def event_attachment_screenshot_filter(
3535
queryset: BaseQuerySet[EventAttachment],
3636
) -> BaseQuerySet[EventAttachment]:
37-
# Intentionally a hardcoded list instead of a regex since current usecases do not have more 3 screenshots
38-
return queryset.filter(
39-
name__in=[
40-
*[f"screenshot{f'-{i}' if i > 0 else ''}.jpg" for i in range(4)],
41-
*[f"screenshot{f'-{i}' if i > 0 else ''}.png" for i in range(4)],
42-
]
43-
)
37+
return queryset.filter(models.Q(name__icontains="screenshot"))
4438

4539

4640
@dataclass(frozen=True)

src/sentry/monitors/serializers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,10 @@ class MonitorCheckInSerializerResponse(MonitorCheckInSerializerResponseOptional)
275275
environment: str
276276
status: str
277277
duration: int
278+
# TODO(epurkhiser): Should be replaced with the actual date_created, right
279+
# now this is the same as dateAdded
278280
dateCreated: datetime
281+
dateAdded: datetime
279282
expectedTime: datetime
280283
monitorConfig: MonitorConfigSerializerResponse
281284

@@ -342,6 +345,7 @@ def serialize(self, obj, attrs, user, **kwargs) -> MonitorCheckInSerializerRespo
342345
"status": obj.get_status_display(),
343346
"duration": obj.duration,
344347
"dateCreated": obj.date_added,
348+
"dateAdded": obj.date_added,
345349
"expectedTime": obj.expected_time,
346350
"monitorConfig": cast(MonitorConfigSerializerResponse, config),
347351
}

src/sentry/receivers/onboarding.py

Lines changed: 28 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@
5757
START_DATE_TRACKING_FIRST_SOURCEMAP_PER_PROJ = datetime(2023, 11, 16, tzinfo=timezone.utc)
5858

5959

60+
def get_owner_id(project: Project, user: RpcUser | None = None) -> int | None:
61+
if user and user.is_authenticated:
62+
return user.id
63+
64+
# this is either the organizations owners user id or None
65+
return Organization.objects.get_from_cache(id=project.organization_id).default_owner_id
66+
67+
6068
@project_created.connect(weak=False, dispatch_uid="record_new_project")
6169
def record_new_project(project, user=None, user_id=None, origin=None, **kwargs):
6270

@@ -69,11 +77,8 @@ def record_new_project(project, user=None, user_id=None, origin=None, **kwargs):
6977
elif user.is_authenticated:
7078
user_id = default_user_id = user.id
7179
else:
72-
user_id = None
73-
try:
74-
default_user = Organization.objects.get(id=project.organization_id).get_default_owner()
75-
default_user_id = default_user.id
76-
except IndexError:
80+
user_id = default_user_id = get_owner_id(project, user)
81+
if user_id is None:
7782
logger.warning(
7883
"Cannot initiate onboarding for organization (%s) due to missing owners",
7984
project.organization_id,
@@ -125,11 +130,7 @@ def record_new_project(project, user=None, user_id=None, origin=None, **kwargs):
125130

126131
@first_event_received.connect(weak=False, dispatch_uid="onboarding.record_first_event")
127132
def record_first_event(project, event, **kwargs):
128-
try:
129-
user: RpcUser = Organization.objects.get_from_cache(
130-
id=project.organization_id
131-
).get_default_owner()
132-
except IndexError:
133+
if (owner_id := get_owner_id(project)) is None:
133134
logger.warning(
134135
"Cannot record first event for organization (%s) due to missing owners",
135136
project.organization_id,
@@ -138,7 +139,7 @@ def record_first_event(project, event, **kwargs):
138139

139140
analytics.record(
140141
"first_event_for_project.sent",
141-
user_id=user.id if user else None,
142+
user_id=owner_id,
142143
organization_id=project.organization_id,
143144
project_id=project.id,
144145
platform=event.platform,
@@ -170,7 +171,7 @@ def record_first_event(project, event, **kwargs):
170171
if created:
171172
analytics.record(
172173
"first_event.sent",
173-
user_id=user.id if user else None,
174+
user_id=owner_id,
174175
organization_id=project.organization_id,
175176
project_id=project.id,
176177
platform=event.platform,
@@ -196,14 +197,9 @@ def record_first_transaction(project, datetime, **kwargs):
196197
date_completed=datetime,
197198
)
198199

199-
try:
200-
default_user_id = project.organization.get_default_owner().id
201-
except IndexError:
202-
default_user_id = None
203-
204200
analytics.record(
205201
"first_transaction.sent",
206-
default_user_id=default_user_id,
202+
default_user_id=get_owner_id(project),
207203
organization_id=project.organization_id,
208204
project_id=project.id,
209205
platform=project.platform,
@@ -216,7 +212,7 @@ def record_first_profile(project, **kwargs):
216212

217213
analytics.record(
218214
"first_profile.sent",
219-
user_id=project.organization.default_owner_id,
215+
user_id=get_owner_id(project),
220216
organization_id=project.organization_id,
221217
project_id=project.id,
222218
platform=project.platform,
@@ -240,7 +236,7 @@ def record_first_replay(project, **kwargs):
240236
logger.info("record_first_replay_analytics_start")
241237
analytics.record(
242238
"first_replay.sent",
243-
user_id=project.organization.default_owner_id,
239+
user_id=get_owner_id(project),
244240
organization_id=project.organization_id,
245241
project_id=project.id,
246242
platform=project.platform,
@@ -266,7 +262,7 @@ def record_first_feedback(project, **kwargs):
266262

267263
analytics.record(
268264
"first_feedback.sent",
269-
user_id=project.organization.default_owner_id,
265+
user_id=get_owner_id(project),
270266
organization_id=project.organization_id,
271267
project_id=project.id,
272268
platform=project.platform,
@@ -281,7 +277,7 @@ def record_first_new_feedback(project, **kwargs):
281277

282278
analytics.record(
283279
"first_new_feedback.sent",
284-
user_id=project.organization.default_owner_id,
280+
user_id=get_owner_id(project),
285281
organization_id=project.organization_id,
286282
project_id=project.id,
287283
platform=project.platform,
@@ -295,7 +291,7 @@ def record_first_cron_monitor(project, user, from_upsert, **kwargs):
295291
if updated:
296292
analytics.record(
297293
"first_cron_monitor.created",
298-
user_id=user.id if user else project.organization.default_owner_id,
294+
user_id=get_owner_id(project, user),
299295
organization_id=project.organization_id,
300296
project_id=project.id,
301297
from_upsert=from_upsert,
@@ -306,7 +302,7 @@ def record_first_cron_monitor(project, user, from_upsert, **kwargs):
306302
def record_cron_monitor_created(project, user, from_upsert, **kwargs):
307303
analytics.record(
308304
"cron_monitor.created",
309-
user_id=user.id if user else project.organization.default_owner_id,
305+
user_id=get_owner_id(project, user),
310306
organization_id=project.organization_id,
311307
project_id=project.id,
312308
from_upsert=from_upsert,
@@ -321,7 +317,7 @@ def record_first_cron_checkin(project, monitor_id, **kwargs):
321317

322318
analytics.record(
323319
"first_cron_checkin.sent",
324-
user_id=project.organization.default_owner_id,
320+
user_id=get_owner_id(project),
325321
organization_id=project.organization_id,
326322
project_id=project.id,
327323
monitor_id=monitor_id,
@@ -354,7 +350,7 @@ def record_first_insight_span(project, module, **kwargs):
354350

355351
analytics.record(
356352
"first_insight_span.sent",
357-
user_id=project.organization.default_owner_id,
353+
user_id=get_owner_id(project),
358354
organization_id=project.organization_id,
359355
project_id=project.id,
360356
platform=project.platform,
@@ -400,10 +396,7 @@ def record_release_received(project, release, **kwargs):
400396
project_id=project.id,
401397
)
402398
if success:
403-
organization = Organization.objects.get_from_cache(id=project.organization_id)
404-
try:
405-
owner: RpcUser = organization.get_default_owner()
406-
except IndexError:
399+
if (owner_id := get_owner_id(project)) is None:
407400
logger.warning(
408401
"Cannot record release received for organization (%s) due to missing owners",
409402
project.organization_id,
@@ -412,7 +405,7 @@ def record_release_received(project, release, **kwargs):
412405

413406
analytics.record(
414407
"first_release_tag.sent",
415-
user_id=owner.id,
408+
user_id=owner_id,
416409
project_id=project.id,
417410
organization_id=project.organization_id,
418411
)
@@ -426,9 +419,7 @@ def record_release_received(project, release, **kwargs):
426419
weak=False, dispatch_uid="onboarding.record_event_with_first_minified_stack_trace_for_project"
427420
)
428421
def record_event_with_first_minified_stack_trace_for_project(project, event, **kwargs):
429-
organization = Organization.objects.get_from_cache(id=project.organization_id)
430-
owner_id = organization.default_owner_id
431-
if not owner_id:
422+
if (owner_id := get_owner_id(project)) is None:
432423
logger.warning(
433424
"Cannot record first event for organization (%s) due to missing owners",
434425
project.organization_id,
@@ -471,18 +462,15 @@ def record_sourcemaps_received(project, event, **kwargs):
471462
project_id=project.id,
472463
)
473464
if success:
474-
organization = Organization.objects.get_from_cache(id=project.organization_id)
475-
try:
476-
owner: RpcUser = organization.get_default_owner()
477-
except IndexError:
465+
if (owner_id := get_owner_id(project)) is None:
478466
logger.warning(
479467
"Cannot record sourcemaps received for organization (%s) due to missing owners",
480468
project.organization_id,
481469
)
482470
return
483471
analytics.record(
484472
"first_sourcemaps.sent",
485-
user_id=owner.id,
473+
user_id=owner_id,
486474
organization_id=project.organization_id,
487475
project_id=project.id,
488476
platform=event.platform,
@@ -498,9 +486,7 @@ def record_sourcemaps_received_for_project(project, event, **kwargs):
498486
if not has_sourcemap(event):
499487
return
500488

501-
organization = Organization.objects.get_from_cache(id=project.organization_id)
502-
owner_id = organization.default_owner_id
503-
if not owner_id:
489+
if (owner_id := get_owner_id(project)) is None:
504490
logger.warning(
505491
"Cannot record sourcemaps received for organization (%s) due to missing owners",
506492
project.organization_id,

src/sentry/utils/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def is_user_signed_request(request: Request) -> bool:
402402

403403
def set_active_org(request: HttpRequest, org_slug: str) -> None:
404404
if options.get("demo-mode.disable-sandbox-redirect"):
405-
if org_slug == "sandbox":
405+
if org_slug in ("sandbox", "telemetry-experience"):
406406
return
407407
# even if the value being set is the same this will trigger a session
408408
# modification and reset the users expiry, so check if they are different first.

static/app/bootstrap/initializeSdk.tsx

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {SENTRY_RELEASE_VERSION, SPA_DSN} from 'sentry/constants';
66
import type {Config} from 'sentry/types/system';
77
import {addExtraMeasurements, addUIElementTag} from 'sentry/utils/performanceForSentry';
88
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
9-
import {getErrorDebugIds} from 'sentry/utils/getErrorDebugIds';
109
import {
1110
createRoutesFromChildren,
1211
matchRoutes,
@@ -192,38 +191,6 @@ export function initializeSdk(config: Config) {
192191
},
193192
});
194193

195-
// Event processor to fill the debug_meta field with debug IDs based on the
196-
// files the error touched. (files inside the stacktrace)
197-
const debugIdPolyfillEventProcessor = async (event: Event, hint: Sentry.EventHint) => {
198-
if (!(hint.originalException instanceof Error)) {
199-
return event;
200-
}
201-
202-
try {
203-
const debugIdMap = await getErrorDebugIds(hint.originalException);
204-
205-
// Fill debug_meta information
206-
event.debug_meta = {};
207-
event.debug_meta.images = [];
208-
const images = event.debug_meta.images;
209-
Object.keys(debugIdMap).forEach(filename => {
210-
images.push({
211-
type: 'sourcemap',
212-
code_file: filename,
213-
debug_id: debugIdMap[filename]!,
214-
});
215-
});
216-
} catch (e) {
217-
event.extra = event.extra || {};
218-
event.extra.debug_id_fetch_error = String(e);
219-
}
220-
221-
return event;
222-
};
223-
debugIdPolyfillEventProcessor.id = 'debugIdPolyfillEventProcessor';
224-
225-
Sentry.addEventProcessor(debugIdPolyfillEventProcessor);
226-
227194
// Track timeOrigin Selection by the SDK to see if it improves transaction durations
228195
Sentry.addEventProcessor((event: Sentry.Event, _hint?: Sentry.EventHint) => {
229196
event.tags = event.tags || {};

static/app/components/core/button/linkButton.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ interface BaseLinkButtonProps extends DO_NOT_USE_CommonButtonProps, LinkElementP
2929
}
3030

3131
interface LinkButtonPropsWithHref extends BaseLinkButtonProps {
32+
href: string;
3233
/**
3334
* Determines if the link is external and should open in a new tab.
3435
*/
3536
external?: boolean;
36-
href?: string;
3737
}
3838

3939
interface LinkButtonPropsWithTo extends BaseLinkButtonProps {
40+
to: string | LocationDescriptor;
4041
/**
4142
* If true, the link will not reset the scroll position of the page when clicked.
4243
*/
@@ -45,7 +46,6 @@ interface LinkButtonPropsWithTo extends BaseLinkButtonProps {
4546
* Determines if the link should replace the current history entry.
4647
*/
4748
replace?: boolean;
48-
to?: string | LocationDescriptor;
4949
}
5050

5151
export type LinkButtonProps = LinkButtonPropsWithHref | LinkButtonPropsWithTo;
@@ -68,7 +68,9 @@ export function LinkButton({
6868
aria-disabled={disabled}
6969
size={size}
7070
{...props}
71+
// @ts-expect-error set href as undefined to force "disabled" state.
7172
href={disabled ? undefined : 'href' in props ? props.href : undefined}
73+
// @ts-expect-error set to as undefined to force "disabled" state
7274
to={disabled ? undefined : 'to' in props ? props.to : undefined}
7375
onClick={handleClick}
7476
>

0 commit comments

Comments
 (0)