Skip to content

Commit 3c74719

Browse files
author
Stephen Cefali
authored
logging(app-context): adds more logging context for slack api errors (#15557)
1 parent 5409878 commit 3c74719

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

src/sentry/integrations/slack/action_endpoint.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ class SlackActionEndpoint(Endpoint):
3131
authentication_classes = ()
3232
permission_classes = ()
3333

34-
def api_error(self, error):
35-
logger.info("slack.action.api-error", extra={"response": error.body})
34+
def api_error(self, error, action_type, logging_data):
35+
logging_data = logging_data.copy()
36+
logging_data["response"] = error.body
37+
logging_data["action_type"] = action_type
38+
logger.info("slack.action.api-error", extra=logging_data)
3639
return self.respond(
3740
{
3841
"response_type": "ephemeral",
@@ -155,11 +158,15 @@ def post(self, request):
155158
channel_id = data.get("channel", {}).get("id")
156159
user_id = data.get("user", {}).get("id")
157160

161+
logging_data["channel_id"] = channel_id
162+
logging_data["user_id"] = user_id
163+
158164
integration = slack_request.integration
159165
logging_data["integration_id"] = integration.id
160166

161167
# Determine the issue group action is being taken on
162168
group_id = slack_request.callback_data["issue"]
169+
logging_data["group_id"] = group_id
163170

164171
# Actions list may be empty when receiving a dialog response
165172
action_list = data.get("actions", [])
@@ -175,6 +182,8 @@ def post(self, request):
175182
logger.error("slack.action.invalid-issue", extra=logging_data)
176183
return self.respond(status=403)
177184

185+
logging_data["organization_id"] = group.organization.id
186+
178187
# Determine the acting user by slack identity
179188
try:
180189
idp = IdentityProvider.objects.get(type="slack", external_id=slack_request.team_id)
@@ -205,7 +214,7 @@ def post(self, request):
205214
try:
206215
self.on_status(request, identity, group, action, data, integration)
207216
except client.ApiError as e:
208-
return self.api_error(e)
217+
return self.api_error(e, "status_dialog", logging_data)
209218

210219
group = Group.objects.get(id=group.id)
211220
attachment = build_group_attachment(group, identity=identity, actions=[action])
@@ -230,6 +239,7 @@ def post(self, request):
230239
defer_attachment_update = False
231240

232241
# Handle interaction actions
242+
action_type = None
233243
try:
234244
for action in action_list:
235245
action_type = action["name"]
@@ -242,7 +252,7 @@ def post(self, request):
242252
self.open_resolve_dialog(data, group, integration)
243253
defer_attachment_update = True
244254
except client.ApiError as e:
245-
return self.api_error(e)
255+
return self.api_error(e, action_type, logging_data)
246256

247257
if defer_attachment_update:
248258
return self.respond()

tests/sentry/integrations/slack/test_action_endpoint.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from six.moves.urllib.parse import parse_qs
66
from mock import patch
77

8+
from sentry.api import client
89
from sentry import options
910
from sentry.models import (
1011
Integration,
@@ -335,6 +336,61 @@ def test_permission_denied(self):
335336
assert not resp.data["replace_original"]
336337
assert resp.data["text"] == "Sentry can't perform that action right now on your behalf!"
337338

339+
@responses.activate
340+
@patch("sentry.api.client.put")
341+
def test_handle_submission_fail(self, client_put):
342+
status_action = {"name": "resolve_dialog", "value": "resolve_dialog"}
343+
344+
# Expect request to open dialog on slack
345+
responses.add(
346+
method=responses.POST,
347+
url="https://slack.com/api/dialog.open",
348+
body='{"ok": true}',
349+
status=200,
350+
content_type="application/json",
351+
)
352+
353+
resp = self.post_webhook(action_data=[status_action])
354+
assert resp.status_code == 200, resp.content
355+
356+
# Opening dialog should *not* cause the current message to be updated
357+
assert resp.content == ""
358+
359+
data = parse_qs(responses.calls[0].request.body)
360+
assert data["token"][0] == self.integration.metadata["access_token"]
361+
assert data["trigger_id"][0] == self.trigger_id
362+
assert "dialog" in data
363+
364+
dialog = json.loads(data["dialog"][0])
365+
callback_data = json.loads(dialog["callback_id"])
366+
assert int(callback_data["issue"]) == self.group1.id
367+
assert callback_data["orig_response_url"] == self.response_url
368+
369+
# Completing the dialog will update the message
370+
responses.add(
371+
method=responses.POST,
372+
url=self.response_url,
373+
body='{"ok": true}',
374+
status=200,
375+
content_type="application/json",
376+
)
377+
378+
# make the client raise an API error
379+
client_put.side_effect = client.ApiError(
380+
403, '{"detail":"You do not have permission to perform this action."}'
381+
)
382+
383+
resp = self.post_webhook(
384+
type="dialog_submission",
385+
callback_id=dialog["callback_id"],
386+
data={"submission": {"resolve_type": "resolved"}},
387+
)
388+
389+
client_put.asser_called()
390+
391+
assert resp.status_code == 200, resp.content
392+
assert resp.data["text"] == "Sentry can't perform that action right now on your behalf!"
393+
338394
def test_invalid_token(self):
339395
resp = self.post_webhook(token="invalid")
340396
assert resp.status_code == 401

0 commit comments

Comments
 (0)