webhooks/sentry: Add back support for the legacy integrations.

When the integration was originally rewritten, support for the
deprecated webhook payloads was removed. We later noticed that some
people using Zulip were still using versions of Sentry that required
the older integration code.

Thus this commit adds back the older integration code and whenever the
Sentry webhook payload does not have a "data" field (which must be
present in all modern payloads as per the documentation at
https://docs.sentry.io/workflow/integrations/integration-platform/webhooks)
we will use the older Sentry integration code.

Signed-off-by: Hemanth V. Alluri <hdrive1999@gmail.com>
This commit is contained in:
Hemanth V. Alluri 2020-05-07 23:22:30 +05:30 committed by Tim Abbott
parent 8411f7f884
commit fb757e91c1
3 changed files with 412 additions and 6 deletions

View File

@ -0,0 +1,375 @@
{
"project": "zulip",
"project_name": "zulip",
"culprit": "raven.scripts.runner in main",
"level": "error",
"url": "https://sentry.io/zulip/zulip/issues/156699934/",
"logger": null,
"message": "This is an example python exception",
"id": "156699934",
"event": {
"received": 1473185565.0,
"sentry.interfaces.User": {
"username": "getsentry",
"id": "1671",
"email": "foo@example.com"
},
"sentry.interfaces.Message": {
"message": "This is an example python exception"
},
"errors": [
],
"extra": {
"emptyList": [
],
"unauthorized": false,
"emptyMap": {
},
"url": "http://example.org/foo/bar/",
"results": [
1,
2,
3,
4,
5
],
"length": 10837790,
"session": {
"foo": "bar"
}
},
"fingerprint": [
"{{ default }}"
],
"modules": {
"my.package": "1.0.0"
},
"sentry.interfaces.Http": {
"cookies": [
[
"foo",
"bar"
],
[
"biz",
"baz"
]
],
"url": "http://example.com/foo",
"headers": [
[
"Content-Type",
"application/json"
],
[
"Referer",
"http://example.com"
],
[
"User-Agent",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36"
]
],
"env": {
"ENV": "prod"
},
"query_string": "foo=bar",
"data": "{\"hello\": \"world\"}",
"method": "GET"
},
"sentry.interfaces.Template": {
"abs_path": "/srv/example/templates/debug_toolbar/base.html",
"pre_context": [
"{% endif %}\n",
"<script src=\"{% static 'debug_toolbar/js/toolbar.js' %}\"></script>\n",
"<div id=\"djDebug\" hidden=\"hidden\" dir=\"ltr\"\n"
],
"post_context": [
" {{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }}>\n",
"\t<div hidden=\"hidden\" id=\"djDebugToolbar\">\n",
"\t\t<ul id=\"djDebugPanelList\">\n"
],
"filename": "debug_toolbar/base.html",
"lineno": 14,
"context_line": " data-store-id=\"{{ toolbar.store_id }}\" data-render-panel-url=\"{% url 'djdt:render_panel' %}\"\n"
},
"version": "5",
"_ref_version": 2,
"_ref": 77799,
"type": "default",
"sentry.interfaces.Stacktrace": {
"frames": [
{
"function": "build_msg",
"abs_path": "/home/ubuntu/.virtualenvs/getsentry/src/raven/raven/base.py",
"pre_context": [
" frames = stack",
"",
" data.update({",
" 'sentry.interfaces.Stacktrace': {",
" 'frames': get_stack_info(frames,"
],
"vars": {
"'frames'": "<generator object iter_stack_frames at 0x107bcc3c0>",
"'culprit'": null,
"'event_type'": "'raven.events.Message'",
"'handler'": "<raven.events.Message object at 0x107bd0890>",
"'date'": "datetime.datetime(2013, 8, 13, 3, 8, 24, 880386)",
"'extra'": {
"'loadavg'": [
0.37255859375,
0.5341796875,
0.62939453125
],
"'user'": "'dcramer'",
"'go_deeper'": [
[
{
"'bar'": "'\\'\\\\\\'[\"\\\\\\\\\\\\\\'baz\\\\\\\\\\\\\\'\"]\\\\\\'\\''",
"'foo'": "'\\'\\\\\\'\"\\\\\\\\\\\\\\'bar\\\\\\\\\\\\\\'\"\\\\\\'\\''"
}
]
]
},
"'v'": {
"'message'": "u'This is a test message generated using ``raven test``'",
"'params'": [
]
},
"'stack'": true,
"'event_id'": "'54a322436e1b47b88e239b78998ae742'",
"'tags'": null,
"'time_spent'": null,
"'self'": "<raven.base.Client object at 0x107bb8210>",
"'data'": {
"'sentry.interfaces.Message'": {
"'message'": "u'This is a test message generated using ``raven test``'",
"'params'": [
]
},
"'message'": "u'This is a test message generated using ``raven test``'"
},
"'result'": {
"'sentry.interfaces.Message'": {
"'message'": "u'This is a test message generated using ``raven test``'",
"'params'": [
]
},
"'message'": "u'This is a test message generated using ``raven test``'"
},
"'kwargs'": {
"'message'": "'This is a test message generated using ``raven test``'",
"'level'": 20
},
"'k'": "'sentry.interfaces.Message'",
"'public_key'": null
},
"module": "raven.base",
"filename": "raven/base.py",
"post_context": [
" },",
" })",
"",
" if 'sentry.interfaces.Stacktrace' in data:",
" if self.include_paths:"
],
"in_app": false,
"context_line": " transformer=self.transform)",
"lineno": 303
},
{
"function": "capture",
"abs_path": "/home/ubuntu/.virtualenvs/getsentry/src/raven/raven/base.py",
"pre_context": [
" if not self.is_enabled():",
" return",
"",
" data = self.build_msg(",
" event_type, data, date, time_spent, extra, stack, tags=tags,"
],
"vars": {
"'event_type'": "'raven.events.Message'",
"'date'": null,
"'extra'": {
"'loadavg'": [
0.37255859375,
0.5341796875,
0.62939453125
],
"'user'": "'dcramer'",
"'go_deeper'": [
[
{
"'bar'": "'\\'\\\\\\'[\"\\\\\\\\\\\\\\'baz\\\\\\\\\\\\\\'\"]\\\\\\'\\''",
"'foo'": "'\\'\\\\\\'\"\\\\\\\\\\\\\\'bar\\\\\\\\\\\\\\'\"\\\\\\'\\''"
}
]
]
},
"'stack'": true,
"'tags'": null,
"'time_spent'": null,
"'self'": "<raven.base.Client object at 0x107bb8210>",
"'data'": null,
"'kwargs'": {
"'message'": "'This is a test message generated using ``raven test``'",
"'level'": 20
}
},
"module": "raven.base",
"filename": "raven/base.py",
"post_context": [
"",
" self.send(**data)",
"",
" return (data.get('event_id'),)",
""
],
"in_app": false,
"context_line": " **kwargs)",
"lineno": 459
},
{
"function": "captureMessage",
"abs_path": "/home/ubuntu/.virtualenvs/getsentry/src/raven/raven/base.py",
"pre_context": [
" \"\"\"",
" Creates an event from ``message``.",
"",
" >>> client.captureMessage('My event just happened!')",
" \"\"\""
],
"vars": {
"'message'": "'This is a test message generated using ``raven test``'",
"'kwargs'": {
"'extra'": {
"'loadavg'": [
0.37255859375,
0.5341796875,
0.62939453125
],
"'user'": "'dcramer'",
"'go_deeper'": [
[
"'\\'\\\\\\'{\"\\\\\\\\\\\\\\'bar\\\\\\\\\\\\\\'\": [\"\\\\\\\\\\\\\\'baz\\\\\\\\\\\\\\'\"], \"\\\\\\\\\\\\\\'foo\\\\\\\\\\\\\\'\": \"\\\\\\\\\\\\\\'bar\\\\\\\\\\\\\\'\"}\\\\\\'\\''"
]
]
},
"'tags'": null,
"'data'": null,
"'level'": 20,
"'stack'": true
},
"'self'": "<raven.base.Client object at 0x107bb8210>"
},
"module": "raven.base",
"filename": "raven/base.py",
"post_context": [
"",
" def captureException(self, exc_info=None, **kwargs):",
" \"\"\"",
" Creates an event from an exception.",
""
],
"in_app": false,
"context_line": " return self.capture('raven.events.Message', message=message, **kwargs)",
"lineno": 577
},
{
"function": "send_test_message",
"abs_path": "/home/ubuntu/.virtualenvs/getsentry/src/raven/raven/scripts/runner.py",
"pre_context": [
" level=logging.INFO,",
" stack=True,",
" tags=options.get('tags', {}),",
" extra={",
" 'user': get_uid(),"
],
"vars": {
"'client'": "<raven.base.Client object at 0x107bb8210>",
"'options'": {
"'tags'": null,
"'data'": null
},
"'data'": null,
"'k'": "'secret_key'"
},
"module": "raven.scripts.runner",
"filename": "raven/scripts/runner.py",
"post_context": [
" },",
" ))",
"",
" if client.state.did_fail():",
" print('error!')"
],
"in_app": false,
"context_line": " 'loadavg': get_loadavg(),",
"lineno": 77
},
{
"function": "main",
"abs_path": "/home/ubuntu/.virtualenvs/getsentry/src/raven/raven/scripts/runner.py",
"pre_context": [
" print(\"Using DSN configuration:\")",
" print(\" \", dsn)",
" print()",
"",
" client = Client(dsn, include_paths=['raven'])"
],
"vars": {
"'root'": "<logging.Logger object at 0x107ba5b10>",
"'parser'": "<argparse.ArgumentParser instance at 0x107ba3368>",
"'dsn'": "'https://ebc35f33e151401f9deac549978bda11:f3403f81e12e4c24942d505f086b2cad@sentry.io/1'",
"'opts'": "<Values at 0x107ba3b00: {'data': None, 'tags': None}>",
"'client'": "<raven.base.Client object at 0x107bb8210>",
"'args'": [
"'test'",
"'https://ebc35f33e151401f9deac549978bda11:f3403f81e12e4c24942d505f086b2cad@sentry.io/1'"
]
},
"module": "raven.scripts.runner",
"filename": "raven/scripts/runner.py",
"lineno": 112,
"in_app": false,
"context_line": " send_test_message(client, opts.__dict__)"
}
],
"has_system_frames": false,
"frames_omitted": null
},
"tags": [
[
"browser",
"Chrome 28.0"
],
[
"device",
"Other"
],
[
"environment",
"production"
],
[
"level",
"error"
],
[
"os",
"Windows 8"
],
[
"sentry:user",
"id:1671"
],
[
"url",
"http://example.com/foo"
]
],
"metadata": {
"title": "This is an example python exception"
}
}
}

View File

@ -156,3 +156,13 @@ Traceback:
expected_topic = "Exception: program has entered an invalid state."
expected_message = """\nIssue **Exception: program has entered an invalid state.** was marked as resolved by **Hemanth V. Alluri**."""
self.send_and_test_stream_message('issue_resolved', expected_topic, expected_message)
def test_deprecated_exception_message(self) -> None:
expected_topic = "zulip"
expected_message = """
New [issue](https://sentry.io/zulip/zulip/issues/156699934/) (level: ERROR):
``` quote
This is an example python exception
```"""
self.send_and_test_stream_message('deprecated_exception_message', expected_topic, expected_message)

View File

@ -9,6 +9,14 @@ from zerver.lib.webhooks.common import UnexpectedWebhookEventType, check_send_we
from zerver.models import UserProfile
DEPRECATED_EXCEPTION_MESSAGE_TEMPLATE = """
New [issue]({url}) (level: {level}):
``` quote
{message}
```
"""
MESSAGE_EVENT_TEMPLATE = """
**New message event:** [{title}]({web_link})
```quote
@ -194,19 +202,32 @@ def handle_issue_payload(action: str, issue: Dict[str, Any], actor: Dict[str, An
return (subject, body)
def handle_deprecated_payload(payload: Dict[str, Any]) -> Tuple[str, str]:
subject = "{}".format(payload.get('project_name'))
body = DEPRECATED_EXCEPTION_MESSAGE_TEMPLATE.format(
level=payload['level'].upper(),
url=payload.get('url'),
message=payload.get('message')
)
return (subject, body)
@api_key_only_webhook_view('Sentry')
@has_request_variables
def api_sentry_webhook(request: HttpRequest, user_profile: UserProfile,
payload: Dict[str, Any] = REQ(argument_type="body")) -> HttpResponse:
data = payload["data"]
data = payload.get("data", None)
# We currently support two types of payloads: events and issues.
if data:
if "event" in data:
subject, body = handle_event_payload(data["event"])
elif "issue" in data:
subject, body = handle_issue_payload(payload["action"], data["issue"], payload["actor"])
else:
raise UnexpectedWebhookEventType("Sentry", str((list(data.keys()))))
else:
subject, body = handle_deprecated_payload(payload)
check_send_webhook_message(request, user_profile, subject, body)
return json_success()