Example #1
0
    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"
Example #3
0
    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"
Example #4
0
    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"
Example #5
0
    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"
Example #6
0
    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)
Example #7
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.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
Example #8
0
    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
Example #11
0
    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"