Skip to content

Commit 0f5e0de

Browse files
committed
improve unfurl slos
1 parent 848dcba commit 0f5e0de

File tree

6 files changed

+100
-88
lines changed

6 files changed

+100
-88
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: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,10 @@ def unfurl_discover(
123123
links: list[UnfurlableUrl],
124124
user: User | RpcUser | None = None,
125125
) -> UnfurledUrl:
126-
event = MessagingInteractionEvent(
126+
with MessagingInteractionEvent(
127127
MessagingInteractionType.UNFURL_DISCOVER, SlackMessagingSpec(), user=user
128-
)
129-
with event.capture():
128+
).capture() as lifecycle:
129+
lifecycle.add_extras({"integration_id": integration.id})
130130
return _unfurl_discover(integration, links, user)
131131

132132

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ def unfurl_issues(
4343
for a particular issue by the URL of the yet-unfurled links a user included
4444
in their Slack message.
4545
"""
46-
event = MessagingInteractionEvent(
46+
with MessagingInteractionEvent(
4747
MessagingInteractionType.UNFURL_ISSUES, SlackMessagingSpec(), user=user
48-
)
49-
with event.capture():
48+
).capture() as lifecycle:
49+
lifecycle.add_extras({"integration_id": integration.id})
5050
return _unfurl_issues(integration, links)
5151

5252

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ def unfurl_metric_alerts(
5757
links: list[UnfurlableUrl],
5858
user: User | RpcUser | None = None,
5959
) -> UnfurledUrl:
60-
event = MessagingInteractionEvent(
60+
with MessagingInteractionEvent(
6161
MessagingInteractionType.UNFURL_METRIC_ALERTS, SlackMessagingSpec(), user=user
62-
)
63-
with event.capture():
62+
).capture() as lifecycle:
63+
lifecycle.add_extras({"integration_id": integration.id})
6464
return _unfurl_metric_alerts(integration, links, user)
6565

6666

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

Lines changed: 79 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from sentry.integrations.slack.unfurl.handlers import link_handlers, match_link
3131
from sentry.integrations.slack.unfurl.types import LinkType, UnfurlableUrl
3232
from sentry.integrations.slack.views.link_identity import build_linking_url
33-
from sentry.integrations.utils.metrics import IntegrationEventLifecycle
3433
from sentry.organizations.services.organization import organization_service
3534

3635
from .base import SlackDMEndpoint
@@ -160,82 +159,89 @@ def on_message(self, request: Request, slack_request: SlackDMRequest) -> Respons
160159

161160
return self.respond()
162161

163-
def on_link_shared(
164-
self, request: Request, slack_request: SlackDMRequest, lifecycle: IntegrationEventLifecycle
165-
) -> bool:
162+
def on_link_shared(self, request: Request, slack_request: SlackDMRequest) -> bool:
166163
"""Returns true on success"""
167164
matches: MutableMapping[LinkType, list[UnfurlableUrl]] = defaultdict(list)
168165
links_seen = set()
169166

170167
data = slack_request.data.get("event", {})
171168

169+
ois = integration_service.get_organization_integrations(
170+
integration_id=slack_request.integration.id, limit=1
171+
)
172+
organization_id = ois[0].organization_id if len(ois) > 0 else None
173+
organization_context = (
174+
organization_service.get_organization_by_id(
175+
id=organization_id,
176+
user_id=None,
177+
include_projects=False,
178+
include_teams=False,
179+
)
180+
if organization_id
181+
else None
182+
)
183+
organization = organization_context.organization if organization_context else None
184+
172185
logger_params = {
173186
"integration_id": slack_request.integration.id,
174187
"team_id": slack_request.team_id,
175188
"channel_id": slack_request.channel_id,
176189
"user_id": slack_request.user_id,
177190
"channel": slack_request.channel_id,
191+
"organization_id": organization_id,
178192
**data,
179193
}
180194

181195
# An unfurl may have multiple links to unfurl
182196
for item in data.get("links", []):
183-
try:
184-
url = item["url"]
185-
except Exception:
186-
_logger.exception("parse-link-error", extra={**logger_params, "url": url})
187-
continue
188-
189-
link_type, args = match_link(url)
190-
191-
# Link can't be unfurled
192-
if link_type is None or args is None:
193-
continue
194-
195-
ois = integration_service.get_organization_integrations(
196-
integration_id=slack_request.integration.id, limit=1
197-
)
198-
organization_id = ois[0].organization_id if len(ois) > 0 else None
199-
organization_context = (
200-
organization_service.get_organization_by_id(
201-
id=organization_id, user_id=None, include_projects=False, include_teams=False
202-
)
203-
if organization_id
204-
else None
205-
)
206-
organization = organization_context.organization if organization_context else None
207-
logger_params["organization_id"] = organization_id
208-
209-
if (
210-
organization
211-
and link_type == LinkType.DISCOVER
212-
and not slack_request.has_identity
213-
and features.has("organizations:discover-basic", organization, actor=request.user)
214-
):
197+
with MessagingInteractionEvent(
198+
interaction_type=MessagingInteractionType.PROCESS_SHARED_LINK,
199+
spec=SlackMessagingSpec(),
200+
).capture() as lifecycle:
215201
try:
216-
analytics.record(
217-
SlackIntegrationChartUnfurl(
218-
organization_id=organization.id,
219-
unfurls_count=0,
220-
)
202+
url = item["url"]
203+
except Exception:
204+
lifecycle.record_failure("Failed to parse link", extra={**logger_params})
205+
continue
206+
207+
link_type, args = match_link(url)
208+
209+
# Link can't be unfurled
210+
if link_type is None or args is None:
211+
lifecycle.record_halt("Unfurlable link", extra={"url": url})
212+
continue
213+
214+
if (
215+
organization
216+
and link_type == LinkType.DISCOVER
217+
and not slack_request.has_identity
218+
and features.has(
219+
"organizations:discover-basic", organization, actor=request.user
221220
)
222-
except Exception as e:
223-
sentry_sdk.capture_exception(e)
221+
):
222+
try:
223+
analytics.record(
224+
SlackIntegrationChartUnfurl(
225+
organization_id=organization.id,
226+
unfurls_count=0,
227+
)
228+
)
229+
except Exception as e:
230+
sentry_sdk.capture_exception(e)
224231

225-
self.prompt_link(slack_request)
226-
return True
232+
self.prompt_link(slack_request)
233+
lifecycle.record_halt("Discover link requires identity", extra={"url": url})
234+
return True
227235

228-
# Don't unfurl the same thing multiple times
229-
seen_marker = hash(orjson.dumps((link_type, list(args))).decode())
230-
if seen_marker in links_seen:
231-
continue
236+
# Don't unfurl the same thing multiple times
237+
seen_marker = hash(orjson.dumps((link_type, list(args))).decode())
238+
if seen_marker in links_seen:
239+
continue
232240

233-
links_seen.add(seen_marker)
234-
matches[link_type].append(UnfurlableUrl(url=url, args=args))
241+
links_seen.add(seen_marker)
242+
matches[link_type].append(UnfurlableUrl(url=url, args=args))
235243

236244
if not matches:
237-
lifecycle.add_extras(logger_params)
238-
lifecycle.record_failure("No matches found")
239245
return False
240246

241247
# Unfurl each link type
@@ -251,8 +257,6 @@ def on_link_shared(
251257
)
252258

253259
if not results:
254-
lifecycle.add_extras(logger_params)
255-
lifecycle.record_failure("No unfurls generated")
256260
return False
257261

258262
# XXX(isabella): we use our message builders to create the blocks for each link to be
@@ -262,21 +266,24 @@ def on_link_shared(
262266
if "text" in link_info:
263267
del link_info["text"]
264268

265-
payload = {"channel": data["channel"], "ts": data["message_ts"], "unfurls": results}
266-
267-
client = SlackSdkClient(integration_id=slack_request.integration.id)
268-
try:
269-
client.chat_unfurl(
270-
channel=data["channel"],
271-
ts=data["message_ts"],
272-
unfurls=payload["unfurls"],
273-
)
274-
except SlackApiError as e:
275-
lifecycle.add_extras(logger_params)
276-
if options.get("slack.log-unfurl-payload", False):
277-
lifecycle.add_extra("unfurls", payload["unfurls"])
278-
lifecycle.record_failure(e)
279-
return False
269+
with MessagingInteractionEvent(
270+
interaction_type=MessagingInteractionType.UNFURL_LINK,
271+
spec=SlackMessagingSpec(),
272+
).capture() as lifecycle:
273+
payload = {"channel": data["channel"], "ts": data["message_ts"], "unfurls": results}
274+
client = SlackSdkClient(integration_id=slack_request.integration.id)
275+
try:
276+
client.chat_unfurl(
277+
channel=data["channel"],
278+
ts=data["message_ts"],
279+
unfurls=payload["unfurls"],
280+
)
281+
except SlackApiError as e:
282+
lifecycle.add_extras(logger_params)
283+
if options.get("slack.log-unfurl-payload", False):
284+
lifecycle.add_extra("unfurls", payload["unfurls"])
285+
lifecycle.record_failure(e)
286+
return False
280287

281288
return True
282289

@@ -291,12 +298,8 @@ def post(self, request: Request) -> Response:
291298
if slack_request.is_challenge():
292299
return self.on_url_verification(request, slack_request.data)
293300
if slack_request.type == "link_shared":
294-
with MessagingInteractionEvent(
295-
interaction_type=MessagingInteractionType.UNFURL_LINK,
296-
spec=SlackMessagingSpec(),
297-
).capture() as lifecycle:
298-
if self.on_link_shared(request, slack_request, lifecycle):
299-
return self.respond()
301+
if self.on_link_shared(request, slack_request):
302+
return self.respond()
300303

301304
if slack_request.type == "message":
302305
if slack_request.is_bot():

tests/sentry/integrations/slack/webhooks/events/test_link_shared.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from sentry.integrations.slack.unfurl.types import Handler, make_type_coercer
1111
from sentry.integrations.types import EventLifecycleOutcome
12-
from sentry.testutils.asserts import assert_slo_metric
12+
from sentry.testutils.asserts import assert_slo_metric_calls
1313

1414
from . import LINK_SHARED_EVENT, BaseEventTest, build_test_block
1515

@@ -108,7 +108,7 @@ def test_share_links_block_kit_sdk(self, mock_match_link, mock_record) -> None:
108108
assert unfurls["link1"] == result1
109109
assert unfurls["link2"] == result2
110110

111-
assert_slo_metric(mock_record, EventLifecycleOutcome.SUCCESS)
111+
assert_slo_metric_calls(mock_record.mock_calls[-2:], EventLifecycleOutcome.SUCCESS)
112112

113113
@patch(
114114
"sentry.integrations.slack.webhooks.event.match_link",
@@ -155,4 +155,12 @@ def test_share_links_failure(self, mock_match_link, mock_record) -> None:
155155

156156
assert resp.status_code == 200, resp.content
157157

158-
assert_slo_metric(mock_record, EventLifecycleOutcome.FAILURE)
158+
assert_slo_metric_calls(mock_record.mock_calls[-2:], EventLifecycleOutcome.FAILURE)
159+
160+
def test_share_links_no_matches(self, mock_record) -> None:
161+
event_data = orjson.loads(LINK_SHARED_EVENT)
162+
event_data["links"] = [{}]
163+
resp = self.post_webhook(event_data=event_data)
164+
assert resp.status_code == 200, resp.content
165+
166+
assert_slo_metric_calls(mock_record.mock_calls[-2:], EventLifecycleOutcome.FAILURE)

0 commit comments

Comments
 (0)