Skip to content

Commit 804a597

Browse files
cathtengshashjar
authored andcommitted
chore(slack): reorganize unfurl SLOs (#102500)
1 parent 941546d commit 804a597

File tree

8 files changed

+152
-123
lines changed

8 files changed

+152
-123
lines changed

src/sentry/integrations/messaging/metrics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class MessagingInteractionType(StrEnum):
3737
VIEW_SUBMISSION = "VIEW_SUBMISSION"
3838

3939
# Automatic behaviors
40+
PROCESS_SHARED_LINK = "PROCESS_SHARED_LINK"
4041
UNFURL_LINK = "UNFURL_LINK"
4142
UNFURL_ISSUES = "UNFURL_ISSUES"
4243
UNFURL_METRIC_ALERTS = "UNFURL_METRIC_ALERTS"

src/sentry/integrations/slack/unfurl/discover.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any
99
from urllib.parse import urlparse
1010

11-
from django.http.request import HttpRequest, QueryDict
11+
from django.http.request import QueryDict
1212

1313
from sentry import analytics, features
1414
from sentry.api import client
@@ -118,15 +118,14 @@ def is_aggregate(field: str) -> bool:
118118

119119

120120
def unfurl_discover(
121-
request: HttpRequest,
122121
integration: Integration | RpcIntegration,
123122
links: list[UnfurlableUrl],
124123
user: User | RpcUser | None = None,
125124
) -> UnfurledUrl:
126-
event = MessagingInteractionEvent(
125+
with MessagingInteractionEvent(
127126
MessagingInteractionType.UNFURL_DISCOVER, SlackMessagingSpec(), user=user
128-
)
129-
with event.capture():
127+
).capture() as lifecycle:
128+
lifecycle.add_extras({"integration_id": integration.id})
130129
return _unfurl_discover(integration, links, user)
131130

132131

src/sentry/integrations/slack/unfurl/issues.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import re
44

5-
from django.http.request import HttpRequest
6-
75
from sentry.integrations.messaging.metrics import (
86
MessagingInteractionEvent,
97
MessagingInteractionType,
@@ -33,7 +31,6 @@
3331

3432

3533
def unfurl_issues(
36-
request: HttpRequest,
3734
integration: Integration | RpcIntegration,
3835
links: list[UnfurlableUrl],
3936
user: User | RpcUser | None = None,
@@ -43,10 +40,10 @@ def unfurl_issues(
4340
for a particular issue by the URL of the yet-unfurled links a user included
4441
in their Slack message.
4542
"""
46-
event = MessagingInteractionEvent(
43+
with MessagingInteractionEvent(
4744
MessagingInteractionType.UNFURL_ISSUES, SlackMessagingSpec(), user=user
48-
)
49-
with event.capture():
45+
).capture() as lifecycle:
46+
lifecycle.add_extras({"integration_id": integration.id})
5047
return _unfurl_issues(integration, links)
5148

5249

src/sentry/integrations/slack/unfurl/metric_alerts.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import sentry_sdk
1010
from django.db.models import Q
11-
from django.http.request import HttpRequest, QueryDict
11+
from django.http.request import QueryDict
1212

1313
from sentry import features
1414
from sentry.api.serializers import serialize
@@ -52,15 +52,14 @@
5252

5353

5454
def unfurl_metric_alerts(
55-
request: HttpRequest,
5655
integration: Integration | RpcIntegration,
5756
links: list[UnfurlableUrl],
5857
user: User | RpcUser | None = None,
5958
) -> UnfurledUrl:
60-
event = MessagingInteractionEvent(
59+
with MessagingInteractionEvent(
6160
MessagingInteractionType.UNFURL_METRIC_ALERTS, SlackMessagingSpec(), user=user
62-
)
63-
with event.capture():
61+
).capture() as lifecycle:
62+
lifecycle.add_extras({"integration_id": integration.id})
6463
return _unfurl_metric_alerts(integration, links, user)
6564

6665

src/sentry/integrations/slack/unfurl/types.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
from re import Pattern
66
from typing import Any, NamedTuple, Optional, Protocol
77

8-
from django.http.request import HttpRequest
9-
108
from sentry.integrations.models.integration import Integration
119
from sentry.integrations.services.integration import RpcIntegration
1210
from sentry.users.models.user import User
@@ -30,7 +28,6 @@ class UnfurlableUrl(NamedTuple):
3028
class HandlerCallable(Protocol):
3129
def __call__(
3230
self,
33-
request: HttpRequest,
3431
integration: Integration | RpcIntegration,
3532
links: list[UnfurlableUrl],
3633
user: User | RpcUser | None = None,

src/sentry/integrations/slack/webhooks/event.py

Lines changed: 103 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import logging
44
from collections import defaultdict
5-
from collections.abc import Mapping, MutableMapping
5+
from collections.abc import Mapping
66
from typing import Any
77

88
import orjson
@@ -31,6 +31,7 @@
3131
from sentry.integrations.slack.unfurl.types import LinkType, UnfurlableUrl
3232
from sentry.integrations.slack.views.link_identity import build_linking_url
3333
from sentry.organizations.services.organization import organization_service
34+
from sentry.organizations.services.organization.model import RpcOrganization
3435

3536
from .base import SlackDMEndpoint
3637
from .command import LINK_FROM_CHANNEL_MESSAGE
@@ -159,108 +160,135 @@ def on_message(self, request: Request, slack_request: SlackDMRequest) -> Respons
159160

160161
return self.respond()
161162

162-
def on_link_shared(self, request: Request, slack_request: SlackDMRequest) -> bool:
163-
"""Returns true on success"""
164-
matches: MutableMapping[LinkType, list[UnfurlableUrl]] = defaultdict(list)
163+
def _get_unfurlable_links(
164+
self,
165+
request: Request,
166+
slack_request: SlackDMRequest,
167+
data: dict[str, Any],
168+
organization: RpcOrganization | None,
169+
logger_params: dict[str, Any],
170+
) -> dict[LinkType, list[UnfurlableUrl]]:
171+
matches: dict[LinkType, list[UnfurlableUrl]] = defaultdict(list)
165172
links_seen = set()
166173

167-
data = slack_request.data.get("event", {})
168-
169-
logger_params = {
170-
"integration_id": slack_request.integration.id,
171-
"team_id": slack_request.team_id,
172-
"channel_id": slack_request.channel_id,
173-
"user_id": slack_request.user_id,
174-
"channel": slack_request.channel_id,
175-
**data,
176-
}
177-
178-
# An unfurl may have multiple links to unfurl
179174
for item in data.get("links", []):
180-
try:
181-
url = item["url"]
182-
except Exception:
183-
_logger.exception("parse-link-error", extra={**logger_params, "url": url})
184-
continue
185-
186-
link_type, args = match_link(url)
187-
188-
# Link can't be unfurled
189-
if link_type is None or args is None:
190-
continue
191-
192-
ois = integration_service.get_organization_integrations(
193-
integration_id=slack_request.integration.id, limit=1
194-
)
195-
organization_id = ois[0].organization_id if len(ois) > 0 else None
196-
organization_context = (
197-
organization_service.get_organization_by_id(
198-
id=organization_id, user_id=None, include_projects=False, include_teams=False
199-
)
200-
if organization_id
201-
else None
202-
)
203-
organization = organization_context.organization if organization_context else None
204-
logger_params["organization_id"] = organization_id
205-
206-
if (
207-
organization
208-
and link_type == LinkType.DISCOVER
209-
and not slack_request.has_identity
210-
and features.has("organizations:discover-basic", organization, actor=request.user)
211-
):
175+
with MessagingInteractionEvent(
176+
interaction_type=MessagingInteractionType.PROCESS_SHARED_LINK,
177+
spec=SlackMessagingSpec(),
178+
).capture() as lifecycle:
212179
try:
213-
analytics.record(
214-
SlackIntegrationChartUnfurl(
215-
organization_id=organization.id,
216-
unfurls_count=0,
217-
)
180+
url = item["url"]
181+
except Exception:
182+
lifecycle.record_failure("Failed to parse link", extra={**logger_params})
183+
continue
184+
185+
link_type, args = match_link(url)
186+
187+
# Link can't be unfurled
188+
if link_type is None or args is None:
189+
lifecycle.record_halt("Unfurlable link", extra={"url": url})
190+
continue
191+
192+
if (
193+
organization
194+
and link_type == LinkType.DISCOVER
195+
and not slack_request.has_identity
196+
and features.has(
197+
"organizations:discover-basic", organization, actor=request.user
218198
)
219-
except Exception as e:
220-
sentry_sdk.capture_exception(e)
199+
):
200+
try:
201+
analytics.record(
202+
SlackIntegrationChartUnfurl(
203+
organization_id=organization.id,
204+
unfurls_count=0,
205+
)
206+
)
207+
except Exception as e:
208+
sentry_sdk.capture_exception(e)
221209

222-
self.prompt_link(slack_request)
223-
return True
210+
self.prompt_link(slack_request)
211+
lifecycle.record_halt("Discover link requires identity", extra={"url": url})
212+
return {}
224213

225-
# Don't unfurl the same thing multiple times
226-
seen_marker = hash(orjson.dumps((link_type, list(args))).decode())
227-
if seen_marker in links_seen:
228-
continue
214+
# Don't unfurl the same thing multiple times
215+
seen_marker = hash(orjson.dumps((link_type, list(args))).decode())
216+
if seen_marker in links_seen:
217+
continue
229218

230-
links_seen.add(seen_marker)
231-
matches[link_type].append(UnfurlableUrl(url=url, args=args))
219+
links_seen.add(seen_marker)
220+
matches[link_type].append(UnfurlableUrl(url=url, args=args))
232221

233-
if not matches:
234-
return False
222+
return matches
235223

236-
# Unfurl each link type
237-
results: MutableMapping[str, Any] = {}
224+
def _unfurl_links(
225+
self, slack_request: SlackDMRequest, matches: dict[LinkType, list[UnfurlableUrl]]
226+
) -> dict[str, Any]:
227+
results: dict[str, Any] = {}
238228
for link_type, unfurl_data in matches.items():
239229
results.update(
240230
link_handlers[link_type].fn(
241-
request,
242-
slack_request.integration,
243-
unfurl_data,
244-
slack_request.user,
231+
slack_request.integration, unfurl_data, slack_request.user
245232
)
246233
)
247234

248-
if not results:
249-
return False
250-
251235
# XXX(isabella): we use our message builders to create the blocks for each link to be
252236
# unfurled, so the original result will include the fallback text string, however, the
253237
# unfurl endpoint does not accept fallback text.
254238
for link_info in results.values():
255239
if "text" in link_info:
256240
del link_info["text"]
257241

258-
payload = {"channel": data["channel"], "ts": data["message_ts"], "unfurls": results}
242+
return results
243+
244+
def on_link_shared(self, request: Request, slack_request: SlackDMRequest) -> bool:
245+
"""Returns true on success"""
246+
247+
data = slack_request.data.get("event", {})
248+
249+
ois = integration_service.get_organization_integrations(
250+
integration_id=slack_request.integration.id, limit=1
251+
)
252+
organization_id = ois[0].organization_id if len(ois) > 0 else None
253+
organization_context = (
254+
organization_service.get_organization_by_id(
255+
id=organization_id,
256+
user_id=None,
257+
include_projects=False,
258+
include_teams=False,
259+
)
260+
if organization_id
261+
else None
262+
)
263+
organization = organization_context.organization if organization_context else None
264+
265+
logger_params = {
266+
"integration_id": slack_request.integration.id,
267+
"team_id": slack_request.team_id,
268+
"channel_id": slack_request.channel_id,
269+
"user_id": slack_request.user_id,
270+
"channel": slack_request.channel_id,
271+
"organization_id": organization_id,
272+
**data,
273+
}
274+
275+
# An unfurl may have multiple links to unfurl
276+
matches = self._get_unfurlable_links(
277+
request, slack_request, data, organization, logger_params
278+
)
279+
if not matches:
280+
return False
281+
282+
# Unfurl each link type
283+
results = self._unfurl_links(slack_request, matches)
284+
if not results:
285+
return False
259286

260287
with MessagingInteractionEvent(
261288
interaction_type=MessagingInteractionType.UNFURL_LINK,
262289
spec=SlackMessagingSpec(),
263290
).capture() as lifecycle:
291+
payload = {"channel": data["channel"], "ts": data["message_ts"], "unfurls": results}
264292
client = SlackSdkClient(integration_id=slack_request.integration.id)
265293
try:
266294
client.chat_unfurl(

0 commit comments

Comments
 (0)