Skip to content

Commit e4d9d75

Browse files
authored
Merge branch 'master' into replay-carousal
2 parents 89e9577 + bc84efe commit e4d9d75

File tree

44 files changed

+452
-280
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+452
-280
lines changed

src/sentry/api/helpers/group_index/update.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from sentry.db.models.query import create_or_update
2626
from sentry.hybridcloud.rpc import coerce_id_from
2727
from sentry.integrations.tasks.kick_off_status_syncs import kick_off_status_syncs
28-
from sentry.issues.grouptype import GroupCategory, get_group_type_by_type_id
28+
from sentry.issues.grouptype import GroupCategory
2929
from sentry.issues.ignored import handle_archived_until_escalating, handle_ignored
3030
from sentry.issues.merge import MergedGroup, handle_merge
3131
from sentry.issues.priority import update_priority
@@ -206,12 +206,9 @@ def update_groups(
206206
status = result.get("status")
207207
res_type = None
208208
if "priority" in result:
209-
if any(
210-
not get_group_type_by_type_id(group.type).enable_user_priority_changes
211-
for group in groups
212-
):
209+
if any(not group.issue_type.enable_user_status_and_priority_changes for group in groups):
213210
return Response(
214-
{"detail": "Cannot manually set priority of a metric issue."},
211+
{"detail": "Cannot manually set priority of one or more issues."},
215212
status=HTTPStatus.BAD_REQUEST,
216213
)
217214

@@ -222,6 +219,12 @@ def update_groups(
222219
project_lookup=project_lookup,
223220
)
224221
if status in ("resolved", "resolvedInNextRelease"):
222+
if any(not group.issue_type.enable_user_status_and_priority_changes for group in groups):
223+
return Response(
224+
{"detail": "Cannot manually resolve one or more issues."},
225+
status=HTTPStatus.BAD_REQUEST,
226+
)
227+
225228
try:
226229
result, res_type = handle_resolve_in_release(
227230
status,

src/sentry/incidents/grouptype.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ class MetricIssue(GroupType):
333333
enable_escalation_detection = False
334334
enable_status_change_workflow_notifications = False
335335
enable_workflow_notifications = False
336-
enable_user_priority_changes = False
336+
enable_user_status_and_priority_changes = False
337337
detector_settings = DetectorSettings(
338338
handler=MetricIssueDetectorHandler,
339339
validator=MetricIssueDetectorValidator,

src/sentry/issues/grouptype.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ class GroupType:
219219
enable_workflow_notifications = True
220220

221221
# Controls whether users are able to manually update the group's priority.
222-
enable_user_priority_changes = True
222+
enable_user_status_and_priority_changes = True
223223

224224
# Controls whether Seer automation is always triggered for this group type.
225225
always_trigger_seer_automation = False

src/sentry/middleware/integrations/parsers/github.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ def get_response(self) -> HttpResponseBase:
100100
if codecov_regions:
101101
self.try_forward_to_codecov(event=event)
102102

103+
logger.info(
104+
"overwatch.debug.forward_if_applicable.begin",
105+
extra={
106+
"headers_keys": list(self.request.headers.keys()),
107+
"integration_id": integration.id,
108+
},
109+
)
103110
# The overwatch forwarder implements its own region-based checks
104111
OverwatchGithubWebhookForwarder(integration=integration).forward_if_applicable(
105112
event=event, headers=self.request.headers

src/sentry/overwatch_webhooks/webhook_forwarder.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,23 @@ def _get_org_summaries_by_region_for_integration(
5353
org_integrations = OrganizationIntegration.objects.filter(
5454
integration=integration, status=ObjectStatus.ACTIVE
5555
)
56+
logger.info(
57+
"overwatch.debug.org_integrations.fetched",
58+
extra={
59+
"integration_id": integration.id,
60+
"org_integration_ids": [oi.organization_id for oi in org_integrations],
61+
"count": len(org_integrations),
62+
},
63+
)
5664
organization_ids = [org_integration.organization_id for org_integration in org_integrations]
5765
org_mappings = OrganizationMapping.objects.filter(organization_id__in=organization_ids)
66+
logger.info(
67+
"overwatch.debug.org_mappings.fetched",
68+
extra={
69+
"org_mapping_ids": [om.organization_id for om in org_mappings],
70+
"count": len(org_mappings),
71+
},
72+
)
5873
org_mappings_by_id = {
5974
org_mapping.organization_id: org_mapping for org_mapping in org_mappings
6075
}
@@ -71,37 +86,87 @@ def _get_org_summaries_by_region_for_integration(
7186
organization_mapping=org_mapping,
7287
)
7388
)
89+
logger.info(
90+
"overwatch.debug.organizations.grouped_by_region",
91+
extra={
92+
"region_name": region_name,
93+
"org_id": org_integration.organization_id,
94+
},
95+
)
96+
97+
logger.info(
98+
"overwatch.debug.org_contexts_by_region.final",
99+
extra={
100+
"regions": list(org_contexts_by_region.keys()),
101+
"counts_per_region": {k: len(v) for k, v in org_contexts_by_region.items()},
102+
},
103+
)
74104

75105
return org_contexts_by_region
76106

77107
def forward_if_applicable(self, event: Mapping[str, Any], headers: Mapping[str, str]):
108+
region_name = None
78109
try:
79110
enabled_regions = options.get("overwatch.enabled-regions")
111+
logger.info(
112+
"overwatch.debug.enabled_regions", extra={"enabled_regions": enabled_regions}
113+
)
80114
if not enabled_regions:
81115
# feature isn't enabled, no work to do
116+
logger.info("overwatch.debug.excluded.feature_not_enabled", extra={})
82117
return
83118

84119
orgs_by_region = self._get_org_summaries_by_region_for_integration(
85120
integration=self.integration
86121
)
122+
logger.info(
123+
"overwatch.debug.orgs_by_region",
124+
extra={
125+
"regions": list(orgs_by_region.keys()),
126+
"counts_per_region": {k: len(v) for k, v in orgs_by_region.items()},
127+
},
128+
)
87129

88130
if not orgs_by_region or not self.should_forward_to_overwatch(headers):
131+
logger.info(
132+
"overwatch.debug.skipped_forwarding",
133+
extra={
134+
"orgs_by_region_empty": not orgs_by_region,
135+
"event_in_forward_list": self.should_forward_to_overwatch(headers),
136+
},
137+
)
89138
return
90139

91140
# We can conditionally opt into forwarding on a per-region basis,
92141
# similar to codecov's current implementation.
93142
for region_name, org_summaries in orgs_by_region.items():
143+
logger.info(
144+
"overwatch.debug.check_region",
145+
extra={
146+
"region_name": region_name,
147+
"enabled": region_name in enabled_regions,
148+
"org_summaries_count": len(org_summaries),
149+
},
150+
)
94151
if region_name not in enabled_regions:
95152
continue
96153

97154
raw_app_id = headers.get(
98155
GITHUB_INSTALLATION_TARGET_ID_HEADER,
99156
) or headers.get(DJANGO_HTTP_GITHUB_INSTALLATION_TARGET_ID_HEADER)
157+
logger.info(
158+
"overwatch.debug.raw_app_id",
159+
extra={"region_name": region_name, "raw_app_id": raw_app_id},
160+
)
100161
app_id: int | None = None
101162
if raw_app_id is not None:
102163
try:
103164
app_id = int(raw_app_id)
104165
except (TypeError, ValueError):
166+
logger.info(
167+
"overwatch.debug.app_id_parse_error",
168+
extra={"region_name": region_name, "raw_app_id": raw_app_id},
169+
)
105170
app_id = None
106171

107172
webhook_detail = WebhookDetails(
@@ -112,17 +177,25 @@ def forward_if_applicable(self, event: Mapping[str, Any], headers: Mapping[str,
112177
region=region_name,
113178
app_id=app_id,
114179
)
180+
logger.info(
181+
"overwatch.debug.webhook_detail.created",
182+
extra={"region_name": region_name, "app_id": app_id},
183+
)
115184

116185
publisher = OverwatchWebhookPublisher(
117186
integration_provider=self.integration.provider,
118187
region=get_region_by_name(region_name),
119188
)
189+
logger.info("overwatch.debug.enqueue_webhook", extra={"region_name": region_name})
120190
publisher.enqueue_webhook(webhook_detail)
121191
metrics.incr(
122192
"overwatch.forward-webhooks.success",
123193
sample_rate=1.0,
124194
tags={"forward_region": region_name},
125195
)
196+
logger.info(
197+
"overwatch.debug.metrics_incr.success", extra={"region_name": region_name}
198+
)
126199
except Exception:
127200
metrics.incr(
128201
"overwatch.forward-webhooks.forward-error",

src/sentry/workflow_engine/endpoints/organization_detector_details.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
)
3636
from sentry.workflow_engine.endpoints.validators.utils import get_unknown_detector_type_error
3737
from sentry.workflow_engine.models import Detector
38-
from sentry.workflow_engine.types import DetectorLifeCycleHooks
3938

4039

4140
def get_detector_validator(
@@ -205,8 +204,6 @@ def delete(self, request: Request, organization: Organization, detector: Detecto
205204
)
206205
validator.delete()
207206

208-
DetectorLifeCycleHooks.on_pending_delete(detector)
209-
210207
if detector.type == MetricIssue.slug:
211208
schedule_update_project_config(detector)
212209

src/sentry/workflow_engine/endpoints/validators/base/detector.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
)
3131
from sentry.workflow_engine.models.data_condition import DataCondition
3232
from sentry.workflow_engine.models.detector import enforce_config_schema
33-
from sentry.workflow_engine.types import DataConditionType, DetectorLifeCycleHooks
33+
from sentry.workflow_engine.types import DataConditionType
3434

3535

3636
@dataclass(frozen=True)
@@ -143,7 +143,6 @@ def update(self, instance: Detector, validated_data: dict[str, Any]):
143143
data=instance.get_audit_log_data(),
144144
)
145145

146-
DetectorLifeCycleHooks.on_after_update(instance)
147146
return instance
148147

149148
def delete(self) -> None:
@@ -230,6 +229,4 @@ def create(self, validated_data):
230229
data=detector.get_audit_log_data(),
231230
)
232231

233-
DetectorLifeCycleHooks.on_after_create(detector)
234-
235232
return detector

src/sentry/workflow_engine/types.py

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
from abc import ABC, abstractmethod
4-
from collections.abc import Callable
54
from dataclasses import dataclass, field
65
from enum import IntEnum, StrEnum
76
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypedDict, TypeVar
@@ -184,54 +183,8 @@ class SnubaQueryDataSourceType(TypedDict):
184183
event_types: list[SnubaQueryEventType]
185184

186185

187-
type DetectorLifeCycleHook = Callable[[Detector], None]
188-
189-
190-
@dataclass(frozen=True)
191-
class DetectorLifeCycleHooks:
192-
"""
193-
The DetectorLifeCycleHooks allow for hooks to be added into different areas of the API.
194-
These hooks are only invoked through the API, for changes that might be happening outside of the API,
195-
it's recommended to use a signal to capture the model updates.
196-
197-
Args:
198-
after_create:
199-
This method is used as a callback after a detector is created. The first argument is the detector that was created.
200-
201-
before_delete:
202-
This method is used in the deletion API for a detector, the callback will be invoked with the id for the detector
203-
that is deleted.
204-
205-
after_update:
206-
This method is used to access when a detector is updated in the API.
207-
"""
208-
209-
after_create: DetectorLifeCycleHook | None = None
210-
pending_delete: DetectorLifeCycleHook | None = None
211-
after_update: DetectorLifeCycleHook | None = None
212-
213-
@staticmethod
214-
def on_after_create(detector: Detector):
215-
hooks = detector.settings.hooks
216-
if hooks and hooks.after_create:
217-
hooks.after_create(detector)
218-
219-
@staticmethod
220-
def on_after_update(detector: Detector):
221-
hooks = detector.settings.hooks
222-
if hooks and hooks.after_update:
223-
hooks.after_update(detector)
224-
225-
@staticmethod
226-
def on_pending_delete(detector: Detector):
227-
hooks = detector.settings.hooks
228-
if hooks and hooks.pending_delete:
229-
hooks.pending_delete(detector)
230-
231-
232186
@dataclass(frozen=True)
233187
class DetectorSettings:
234-
hooks: DetectorLifeCycleHooks | None = None
235188
handler: type[DetectorHandler] | None = None
236189
validator: type[BaseDetectorTypeValidator] | None = None
237190
config_schema: dict[str, Any] = field(default_factory=dict)

src/sentry/workflow_engine/typings/grouptype.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ class IssueStreamGroupType(GroupType):
1717
enable_escalation_detection = False
1818
enable_status_change_workflow_notifications = False
1919
enable_workflow_notifications = False
20-
enable_user_priority_changes = False
20+
enable_user_status_and_priority_changes = False

static/app/__mocks__/api.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,7 @@ class Client implements ApiNamespace.Client {
275275
abort: () => {},
276276
then: () => {},
277277
error: () => {},
278-
},
279-
new XMLHttpRequest()
278+
}
280279
);
281280

282281
this.handleRequestError(

0 commit comments

Comments
 (0)