def test_makes_successful_request(self): responses.add( method=responses.POST, url="https://example.com/sentry/alert-rule", status=200, json="Saved information", ) result = AlertRuleActionRequester.run( install=self.install, uri="/sentry/alert-rule", fields=self.fields, ) assert result["success"] assert result["message"] == 'foo: "Saved information"' request = responses.calls[0].request data = { "fields": [ {"name": "title", "value": "An Alert"}, {"name": "description", "value": "threshold reached"}, { "name": "assignee_id", "value": "user-1", }, ], "installationId": self.install.uuid, } payload = json.loads(request.body) assert payload == data assert request.headers["Sentry-App-Signature"] == self.sentry_app.build_signature( json.dumps(payload) ) buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 200 assert requests[0]["event_type"] == "alert_rule_action.requested"
def test_makes_request(self): fields = { "title": "An Alert", "description": "threshold reached", "assignee": "user-1" } responses.add( method=responses.POST, url="https://example.com/sentry/alert-rule", status=200, json={}, ) result = AlertRuleActionRequester.run( install=self.install, uri="/sentry/alert-rule", fields=fields, ) assert result["success"] request = responses.calls[0].request assert request.headers["Sentry-App-Signature"] data = { "fields": { "title": "An Alert", "description": "threshold reached", "assignee": "user-1", }, "installationId": self.install.uuid, } payload = json.loads(request.body) assert payload == data buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 200 assert requests[0]["event_type"] == "alert_rule_action.requested"
def test_makes_request(self): options = [ { "label": "An Issue", "value": "123", "default": True }, { "label": "Another Issue", "value": "456" }, ] responses.add( method=responses.GET, url= u"https://example.com/get-issues?installationId={}&projectSlug={}". format(self.install.uuid, self.project.slug), json=options, status=200, content_type="application/json", ) result = SelectRequester.run(install=self.install, project=self.project, uri="/get-issues") assert result == { "choices": [["123", "An Issue"], ["456", "Another Issue"]], "defaultValue": "123", } request = responses.calls[0].request assert request.headers["Sentry-App-Signature"] buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 200 assert requests[0]["event_type"] == "select_options.requested"
def test_500_response(self): responses.add( method=responses.GET, url=f"https://example.com/get-issues?installationId={self.install.uuid}&projectSlug={self.project.slug}", body="Something failed", status=500, ) with self.assertRaises(APIError): SelectRequester.run( install=self.install, project=self.project, group=self.group, uri="/get-issues", fields={}, ) buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 500 assert requests[0]["event_type"] == "select_options.requested"
def test_500_response(self): responses.add( method=responses.POST, url="https://example.com/link-issue", body="Something failed", status=500, ) with self.assertRaises(APIError): IssueLinkRequester.run( install=self.install, project=self.project, group=self.group, uri="/link-issue", fields={}, user=self.user, action="create", ) buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 500 assert requests[0]["event_type"] == "external_issue.created"
def get(self, request: Request, sentry_app) -> Response: """ :qparam string eventType: Optionally specify a specific event type to filter requests :qparam bool errorsOnly: If this is true, only return error/warning requests (300-599) """ event_type = request.GET.get("eventType") errors_only = request.GET.get("errorsOnly") kwargs = {} if event_type: if event_type not in EXTENDED_VALID_EVENTS: return Response({"detail": "Invalid event type."}, status=400) kwargs["event"] = event_type if errors_only: kwargs["errors_only"] = True buffer = SentryAppWebhookRequestsBuffer(sentry_app) formatted_requests = [ self.format_request(req, sentry_app) for req in buffer.get_requests(**kwargs) ] return Response(formatted_requests)
class TestWebhookRequests(TestCase): def setUp(self): self.project = self.create_project() self.user = self.create_user() self.sentry_app = self.create_sentry_app( name="Test App", organization=self.project.organization, events=["issue.resolved", "issue.ignored", "issue.assigned"], ) self.install = self.create_sentry_app_installation( organization=self.project.organization, slug=self.sentry_app.slug) self.issue = self.create_group(project=self.project) self.buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockFailureResponseInstance) def test_saves_error_if_webhook_request_fails(self, safe_urlopen): data = {"issue": serialize(self.issue)} send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 400 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockResponseInstance) def test_saves_request_if_webhook_request_succeeds(self, safe_urlopen): data = {"issue": serialize(self.issue)} send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 200 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id @patch("sentry.tasks.sentry_apps.safe_urlopen", side_effect=RequestException("Timeout")) def test_saves_error_for_request_timeout(self, safe_urlopen): data = {"issue": serialize(self.issue)} with self.assertRaises(RequestException): send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 0 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id
def test_send_alert_event(self, safe_urlopen): group = self.create_group(project=self.project) event = self.create_event(group=group) rule_future = RuleFuture(rule=self.rule, kwargs={"sentry_app": self.sentry_app}) with self.tasks(): notify_sentry_app(event, [rule_future]) data = json.loads(faux(safe_urlopen).kwargs["data"]) assert data == { "action": "triggered", "installation": { "uuid": self.install.uuid }, "data": { "event": DictContaining( event_id=event.event_id, url=absolute_uri( reverse( "sentry-api-0-project-event-details", args=[ self.organization.slug, self.project.slug, event.event_id ], )), web_url=absolute_uri( reverse( "sentry-organization-event-detail", args=[ self.organization.slug, group.id, event.event_id ], )), issue_url=absolute_uri("/api/0/issues/{}/".format( group.id)), ), "triggered_rule": self.rule.label, }, "actor": { "type": "application", "id": "sentry", "name": "Sentry" }, } assert faux(safe_urlopen).kwarg_equals( "headers", DictContaining( "Content-Type", "Request-ID", "Sentry-Hook-Resource", "Sentry-Hook-Timestamp", "Sentry-Hook-Signature", ), ) buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 200 assert requests[0]["event_type"] == "event_alert.triggered"
class TestSentryAppWebhookRequests(TestCase): def setUp(self): self.sentry_app = self.create_sentry_app( name="Test App", events=["issue.resolved", "issue.ignored", "issue.assigned"]) self.project = self.create_project() self.buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) def test_only_100_entries_in_buffer(self): for i in range(100): self.buffer.add_request(200, i, "issue.assigned", "https://example.com/hook") requests = self.buffer.get_requests() assert len(requests) == 100 assert requests[0]["organization_id"] == 99 assert requests[99]["organization_id"] == 0 self.buffer.add_request(500, 100, "issue.assigned", "https://test.com/hook") requests = self.buffer.get_requests() assert len(requests) == 100 assert requests[0]["organization_id"] == 100 assert requests[0]["response_code"] == 500 assert requests[99]["organization_id"] == 1 assert requests[99]["response_code"] == 200 def test_error_added(self): self.buffer.add_request( 200, 1, "issue.assigned", "https://example.com/hook", error_id="d5111da2c28645c5889d072017e3445d", project_id=1, ) requests = self.buffer.get_requests() assert len(requests) == 1 assert requests[0]["error_id"] == "d5111da2c28645c5889d072017e3445d" assert requests[0]["project_id"] == 1 def test_error_not_added_if_project_id_missing(self): self.buffer.add_request( 200, 1, "issue.assigned", "https://example.com/hook", error_id="d5111da2c28645c5889d072017e3445d", ) requests = self.buffer.get_requests() assert len(requests) == 1 assert "error_id" not in requests[0] assert "project_id" not in requests[0] def test_error_not_added_if_error_id_missing(self): self.buffer.add_request(200, 1, "issue.assigned", "https://example.com/hook", project_id=1) requests = self.buffer.get_requests() assert len(requests) == 1 assert "error_id" not in requests[0] assert "project_id" not in requests[0]
class TestWebhookRequests(TestCase): def setUp(self): self.project = self.create_project() self.user = self.create_user() self.sentry_app = self.create_sentry_app( name="Test App", organization=self.project.organization, events=["issue.resolved", "issue.ignored", "issue.assigned"], ) self.sentry_app.update(status=SentryAppStatus.PUBLISHED) self.install = self.create_sentry_app_installation( organization=self.project.organization, slug=self.sentry_app.slug) self.issue = self.create_group(project=self.project) self.buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockFailureResponseInstance) def test_saves_error_if_webhook_request_fails(self, safe_urlopen): data = {"issue": serialize(self.issue)} with self.assertRaises(ClientError): send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 400 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockResponseInstance) def test_saves_request_if_webhook_request_succeeds(self, safe_urlopen): data = {"issue": serialize(self.issue)} send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 200 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id @patch("sentry.tasks.sentry_apps.safe_urlopen", side_effect=Timeout) def test_saves_error_for_request_timeout(self, safe_urlopen): data = {"issue": serialize(self.issue)} # we don't log errors for unpublished and internal apps with self.assertRaises(Timeout): send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 0 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockResponseWithHeadersInstance) def test_saves_error_event_id_if_in_header(self, safe_urlopen): data = {"issue": serialize(self.issue)} with self.assertRaises(ClientError): send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 400 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id assert first_request["error_id"] == "d5111da2c28645c5889d072017e3445d" assert first_request["project_id"] == "1" @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockResponse404) def test_raises_ignorable_error_for_internal_apps(self, safe_urlopen): data = {"issue": serialize(self.issue)} self.sentry_app.update(status=SentryAppStatus.INTERNAL) with self.assertRaises(IgnorableSentryAppError): send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 404 assert first_request["event_type"] == "issue.assigned" @patch("sentry.tasks.sentry_apps.safe_urlopen", return_value=MockResponse404) def test_raises_ignorable_error_for_unpublished_apps(self, safe_urlopen): data = {"issue": serialize(self.issue)} self.sentry_app.update(status=SentryAppStatus.UNPUBLISHED) with self.assertRaises(IgnorableSentryAppError): send_webhooks(installation=self.install, event="issue.assigned", data=data, actor=self.user) requests = self.buffer.get_requests() requests_count = len(requests) first_request = requests[0] assert safe_urlopen.called assert requests_count == 1 assert first_request["response_code"] == 404 assert first_request["event_type"] == "issue.assigned" assert first_request["organization_id"] == self.install.organization.id
def test_makes_request(self): fields = { "title": "An Issue", "description": "a bug was found", "assignee": "user-1" } responses.add( method=responses.POST, url="https://example.com/link-issue", json={ "project": "ProjectName", "webUrl": "https://example.com/project/issue-id", "identifier": "issue-1", }, status=200, content_type="application/json", ) result = IssueLinkRequester.run( install=self.install, project=self.project, group=self.group, uri="/link-issue", fields=fields, user=self.user, action="create", ) assert result == { "project": "ProjectName", "webUrl": "https://example.com/project/issue-id", "identifier": "issue-1", } request = responses.calls[0].request data = { "fields": { "title": "An Issue", "description": "a bug was found", "assignee": "user-1" }, "issueId": self.group.id, "installationId": self.install.uuid, "webUrl": self.group.get_absolute_url(), "project": { "id": self.project.id, "slug": self.project.slug }, "actor": { "type": "user", "id": self.user.id, "name": self.user.name }, } payload = json.loads(request.body) assert payload == data assert request.headers[ "Sentry-App-Signature"] == self.sentry_app.build_signature( json.dumps(payload)) buffer = SentryAppWebhookRequestsBuffer(self.sentry_app) requests = buffer.get_requests() assert len(requests) == 1 assert requests[0]["response_code"] == 200 assert requests[0]["event_type"] == "external_issue.created"