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_linked_error_not_returned_if_project_does_not_exist(self):
        self.login_as(user=self.user)

        self.store_event(
            data={
                "event_id": self.event_id,
                "timestamp": iso_format(before_now(minutes=1))
            },
            project_id=self.project.id,
        )

        buffer = SentryAppWebhookRequestsBuffer(self.published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unpublished_app.webhook_url,
            error_id=self.event_id,
            project_id="1000",
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.published_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 1
        assert response.data[0]["organization"]["slug"] == self.org.slug
        assert response.data[0]["sentryAppSlug"] == self.published_app.slug
        assert "errorUrl" not in response.data[0]
Example #3
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 #4
0
    def test_500_response(self):
        responses.add(
            method=responses.GET,
            url=
            u"https://example.com/get-issues?installationId={}&projectSlug={}".
            format(self.install.uuid, 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_makes_request(self):
        options = [
            {"label": "An Issue", "value": "123", "default": True},
            {"label": "Another Issue", "value": "456"},
        ]
        responses.add(
            method=responses.GET,
            url=f"https://example.com/get-issues?installationId={self.install.uuid}&projectSlug={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_send_alert_event_with_additional_payload(self, safe_urlopen):
        event = self.store_event(data={}, project_id=self.project.id)
        settings = {
            "alert_prefix": "[Not Good]",
            "channel": "#ignored-errors",
            "best_emoji": ":fire:",
        }
        rule_future = RuleFuture(
            rule=self.rule,
            kwargs={"sentry_app": self.sentry_app, "schema_defined_settings": settings},
        )

        with self.tasks():
            notify_sentry_app(event, [rule_future])

        payload = json.loads(faux(safe_urlopen).kwargs["data"])

        assert payload["action"] == "triggered"
        assert payload["data"]["triggered_rule"] == self.rule.label
        assert payload["data"]["issue_alert"] == {
            "id": self.rule.id,
            "title": self.rule.label,
            "sentry_app_id": self.sentry_app.id,
            "settings": settings,
        }

        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"
Example #7
0
    def get(self, request, sentry_app):
        """
        :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 #8
0
    def test_webhook_request_saved(self, safe_urlopen):
        InstallationNotifier.run(install=self.install, user=self.user, action="created")
        InstallationNotifier.run(install=self.install, user=self.user, action="deleted")

        buffer = SentryAppWebhookRequestsBuffer(self.sentry_app)
        requests = buffer.get_requests()

        assert len(requests) == 2
        assert requests[0]["event_type"] == "installation.deleted"
        assert requests[1]["event_type"] == "installation.created"
    def test_send_alert_event(self, safe_urlopen):
        event = self.store_event(data={}, project_id=self.project.id)
        group = event.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(f"/api/0/issues/{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"
Example #10
0
    def get(self, request, sentry_app):

        # TODO add optional query params for event type
        # for now I'm just getting all requests for all events

        buffer = SentryAppWebhookRequestsBuffer(sentry_app)

        formatted_requests = [
            self.format_request(req, sentry_app)
            for req in buffer.get_requests()
        ]

        return Response(formatted_requests)
    def test_user_does_not_see_unowned_published_requests(self):
        self.login_as(user=self.user)

        buffer = SentryAppWebhookRequestsBuffer(self.unowned_published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unowned_published_app.webhook_url,
        )

        url = reverse("sentry-api-0-sentry-app-requests", args=[self.unowned_published_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 403
        assert response.data["detail"] == "You do not have permission to perform this action."
    def test_user_sees_owned_unpublished_requests(self):
        self.login_as(user=self.user)

        buffer = SentryAppWebhookRequestsBuffer(self.unpublished_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unpublished_app.webhook_url,
        )

        url = reverse("sentry-api-0-sentry-app-requests", args=[self.unpublished_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 1
Example #13
0
    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)
    def test_internal_app_requests_does_not_have_organization_field(self):
        self.login_as(user=self.user)
        buffer = SentryAppWebhookRequestsBuffer(self.internal_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.internal_app.webhook_url,
        )

        url = reverse("sentry-api-0-sentry-app-requests", args=[self.internal_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 1
        assert "organization" not in response.data[0]
        assert response.data[0]["sentryAppSlug"] == self.internal_app.slug
        assert response.data[0]["responseCode"] == 200
Example #15
0
    def test_makes_failed_request(self):

        responses.add(
            method=responses.POST,
            url="https://example.com/sentry/alert-rule",
            status=401,
            json="Channel not found!",
        )

        result = AlertRuleActionRequester.run(
            install=self.install,
            uri="/sentry/alert-rule",
            fields=self.fields,
        )
        assert not result["success"]
        assert result["message"] == 'foo: "Channel not found!"'
        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"] == 401
        assert requests[0]["event_type"] == "alert_rule_action.requested"
    def test_linked_error_not_returned_if_project_does_not_exist(self):
        self.login_as(user=self.user)

        buffer = SentryAppWebhookRequestsBuffer(self.published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unpublished_app.webhook_url,
            error_id="d5111da2c28645c5889d072017e3445d",
            project_id="1000",
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.published_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 1
        assert response.data[0]["organization"]["slug"] == self.org.slug
        assert response.data[0]["sentryAppSlug"] == self.published_app.slug
        assert "errorUrl" not in response.data[0]
    def test_event_type_filter(self):
        self.login_as(user=self.user)
        buffer = SentryAppWebhookRequestsBuffer(self.published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.published_app.webhook_url,
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.published_app.slug])
        response1 = self.client.get("{}?eventType=issue.created".format(url),
                                    format="json")
        assert response1.status_code == 200
        assert len(response1.data) == 0

        response2 = self.client.get("{}?eventType=issue.assigned".format(url),
                                    format="json")
        assert response2.status_code == 200
        assert len(response2.data) == 1
class TestSentryAppWebhookRequests(TestCase):
    def setUp(self):
        self.sentry_app = self.create_sentry_app(
            name="Test App", events=["issue.resolved", "issue.ignored", "issue.assigned"]
        )

        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_superuser_sees_unowned_published_requests(self):
        self.login_as(user=self.superuser, superuser=True)

        buffer = SentryAppWebhookRequestsBuffer(self.unowned_published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unowned_published_app.webhook_url,
        )
        buffer.add_request(
            response_code=500,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unowned_published_app.webhook_url,
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.unowned_published_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 2
        assert response.data[0]["organization"]["slug"] == self.org.slug
        assert response.data[0][
            "sentryAppSlug"] == self.unowned_published_app.slug
        assert response.data[0]["responseCode"] == 500
Example #20
0
def send_and_save_sentry_app_request(url, sentry_app, org_id, event, **kwargs):
    """
    Send a webhook request, and save the request into the Redis buffer for the app dashboard request log
    Returns the response of the request

    kwargs ends up being the arguments passed into safe_urlopen
    """

    buffer = SentryAppWebhookRequestsBuffer(sentry_app)

    try:
        resp = safe_urlopen(url=url, **kwargs)
    except RequestException:
        # Response code of 0 represents timeout
        buffer.add_request(response_code=0,
                           org_id=org_id,
                           event=event,
                           url=url)
        # Re-raise the exception because some of these tasks might retry on the exception
        raise

    buffer.add_request(response_code=resp.status_code,
                       org_id=org_id,
                       event=event,
                       url=url)

    return resp
    def test_errors_only_filter(self):
        self.login_as(user=self.user)
        buffer = SentryAppWebhookRequestsBuffer(self.published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.published_app.webhook_url,
        )
        buffer.add_request(
            response_code=500,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.published_app.webhook_url,
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.published_app.slug])
        errors_only_response = self.client.get(
            "{}?errorsOnly=true".format(url), format="json")
        assert errors_only_response.status_code == 200
        assert len(errors_only_response.data) == 1

        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 2
Example #22
0
def send_and_save_sentry_app_request(url, sentry_app, org_id, event, **kwargs):
    """
    Send a webhook request, and save the request into the Redis buffer for the app dashboard request log
    Returns the response of the request

    kwargs ends up being the arguments passed into safe_urlopen
    """

    buffer = SentryAppWebhookRequestsBuffer(sentry_app)

    slug = sentry_app.slug_for_metrics

    try:
        resp = safe_urlopen(url=url, **kwargs)
    except RequestException:
        track_response_code("timeout", slug, event)
        # Response code of 0 represents timeout
        buffer.add_request(response_code=0,
                           org_id=org_id,
                           event=event,
                           url=url)
        # Re-raise the exception because some of these tasks might retry on the exception
        raise

    track_response_code(resp.status_code, slug, event)
    buffer.add_request(
        response_code=resp.status_code,
        org_id=org_id,
        event=event,
        url=url,
        error_id=resp.headers.get("Sentry-Hook-Error"),
        project_id=resp.headers.get("Sentry-Hook-Project"),
    )

    return resp
    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 #24
0
def send_and_save_webhook_request(sentry_app, app_platform_event, url=None):
    """
    Notify a SentryApp's webhook about an incident and log response on redis.

    :param sentry_app: The SentryApp to notify via a webhook.
    :param app_platform_event: Incident data. See AppPlatformEvent.
    :param url: The URL to hit for this webhook if it is different from `sentry_app.webhook_url`.
    :return: Webhook response
    """
    buffer = SentryAppWebhookRequestsBuffer(sentry_app)

    org_id = app_platform_event.install.organization_id
    event = f"{app_platform_event.resource}.{app_platform_event.action}"
    slug = sentry_app.slug_for_metrics
    url = url or sentry_app.webhook_url

    try:
        resp = safe_urlopen(
            url=url, data=app_platform_event.body, headers=app_platform_event.headers, timeout=5
        )

    except (Timeout, ConnectionError) as e:
        error_type = e.__class__.__name__.lower()
        logger.info(
            "send_and_save_webhook_request.timeout",
            extra={
                "error_type": error_type,
                "organization_id": org_id,
                "integration_slug": sentry_app.slug,
            },
        )
        track_response_code(error_type, slug, event)
        # Response code of 0 represents timeout
        buffer.add_request(response_code=0, org_id=org_id, event=event, url=url)
        # Re-raise the exception because some of these tasks might retry on the exception
        raise

    else:
        track_response_code(resp.status_code, slug, event)
        buffer.add_request(
            response_code=resp.status_code,
            org_id=org_id,
            event=event,
            url=url,
            error_id=resp.headers.get("Sentry-Hook-Error"),
            project_id=resp.headers.get("Sentry-Hook-Project"),
        )

        if resp.status_code == 503:
            raise ApiHostError.from_request(resp.request)

        elif resp.status_code == 504:
            raise ApiTimeoutError.from_request(resp.request)

        if 400 <= resp.status_code < 500:
            raise ClientError(resp.status_code, url, response=resp)

        resp.raise_for_status()

        return resp
Example #25
0
def send_and_save_webhook_request(url, sentry_app, app_platform_event):
    buffer = SentryAppWebhookRequestsBuffer(sentry_app)

    org_id = app_platform_event.install.organization_id
    event = "{}.{}".format(app_platform_event.resource,
                           app_platform_event.action)

    try:
        resp = safe_urlopen(url=url,
                            data=app_platform_event.body,
                            headers=app_platform_event.headers,
                            timeout=5)
    except RequestException:
        # Response code of 0 represents timeout
        buffer.add_request(response_code=0,
                           org_id=org_id,
                           event=event,
                           url=url)
        # Re-raise the exception because some of these tasks might retry on the exception
        raise

    buffer.add_request(
        response_code=resp.status_code,
        org_id=org_id,
        event=event,
        url=url,
        error_id=resp.headers.get("Sentry-Hook-Error"),
        project_id=resp.headers.get("Sentry-Hook-Project"),
    )

    return resp
Example #26
0
    def test_linked_error_not_returned_if_event_does_not_exist(self):
        self.login_as(user=self.user)

        # event_id doesn't correspond to an existing event because we didn't call store_event

        buffer = SentryAppWebhookRequestsBuffer(self.published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unpublished_app.webhook_url,
            error_id=self.event_id,
            project_id=self.project.id,
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.published_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 1
        assert response.data[0]["organization"]["slug"] == self.org.slug
        assert response.data[0]["sentryAppSlug"] == self.published_app.slug
        assert "errorUrl" not in response.data[0]
Example #27
0
    def test_linked_error_not_returned_if_project_doesnt_belong_to_org(self):
        self.login_as(user=self.user)
        unowned_project = self.create_project(
            organization=self.create_organization())

        buffer = SentryAppWebhookRequestsBuffer(self.published_app)
        buffer.add_request(
            response_code=200,
            org_id=self.org.id,
            event="issue.assigned",
            url=self.unpublished_app.webhook_url,
            error_id=self.event_id,
            project_id=unowned_project.id,
        )

        url = reverse("sentry-api-0-sentry-app-requests",
                      args=[self.published_app.slug])
        response = self.client.get(url, format="json")
        assert response.status_code == 200
        assert len(response.data) == 1
        assert response.data[0]["organization"]["slug"] == self.org.slug
        assert response.data[0]["sentryAppSlug"] == self.published_app.slug
        assert "errorUrl" not in response.data[0]
Example #28
0
def send_webhooks(installation, event, **kwargs):
    try:
        servicehook = ServiceHook.objects.get(
            organization_id=installation.organization_id, actor_id=installation.id
        )
    except ServiceHook.DoesNotExist:
        return

    if event not in servicehook.events:
        return

    # The service hook applies to all projects if there are no
    # ServiceHookProject records. Otherwise we want check if
    # the event is within the allowed projects.
    project_limited = ServiceHookProject.objects.filter(service_hook_id=servicehook.id).exists()

    if not project_limited:
        resource, action = event.split(".")

        kwargs["resource"] = resource
        kwargs["action"] = action
        kwargs["install"] = installation

        request_data = AppPlatformEvent(**kwargs)

        buffer = SentryAppWebhookRequestsBuffer(installation.sentry_app)

        try:
            resp = safe_urlopen(
                url=servicehook.sentry_app.webhook_url,
                data=request_data.body,
                headers=request_data.headers,
                timeout=5,
            )
        except RequestException:
            # Response code of 0 represents timeout
            buffer.add_request(
                response_code=0,
                org_id=installation.organization_id,
                event=event,
                url=servicehook.sentry_app.webhook_url,
            )
            # Re-raise the exception because some of these tasks might retry on the exception
            raise

        buffer.add_request(
            response_code=resp.status_code,
            org_id=installation.organization_id,
            event=event,
            url=servicehook.sentry_app.webhook_url,
        )
Example #29
0
def send_and_save_sentry_app_request(url, sentry_app, org_id, event, **kwargs):
    """
    Send a webhook request, and save the request into the Redis buffer for the app dashboard request log
    Returns the response of the request

    kwargs ends up being the arguments passed into safe_urlopen
    """

    buffer = SentryAppWebhookRequestsBuffer(sentry_app)

    slug = sentry_app.slug_for_metrics

    try:
        resp = safe_urlopen(url=url, **kwargs)

    except (Timeout, ConnectionError) as e:
        error_type = e.__class__.__name__.lower()
        logger.info(
            "send_and_save_sentry_app_request.timeout",
            extra={
                "error_type": error_type,
                "organization_id": org_id,
                "integration_slug": sentry_app.slug,
            },
        )
        track_response_code(error_type, slug, event)
        # Response code of 0 represents timeout
        buffer.add_request(response_code=0,
                           org_id=org_id,
                           event=event,
                           url=url)
        # Re-raise the exception because some of these tasks might retry on the exception
        raise

    else:
        track_response_code(resp.status_code, slug, event)
        buffer.add_request(
            response_code=resp.status_code,
            org_id=org_id,
            event=event,
            url=url,
            error_id=resp.headers.get("Sentry-Hook-Error"),
            project_id=resp.headers.get("Sentry-Hook-Project"),
        )
        resp.raise_for_status()
        return resp
Example #30
0
def send_and_save_webhook_request(url, sentry_app, app_platform_event):
    buffer = SentryAppWebhookRequestsBuffer(sentry_app)

    org_id = app_platform_event.install.organization_id
    event = "{}.{}".format(app_platform_event.resource, app_platform_event.action)
    slug = sentry_app.slug_for_metrics

    try:
        resp = safe_urlopen(
            url=url, data=app_platform_event.body, headers=app_platform_event.headers, timeout=5
        )

    except (Timeout, ConnectionError) as e:
        track_response_code(e.__class__.__name__.lower(), slug, event)
        # Response code of 0 represents timeout
        buffer.add_request(response_code=0, org_id=org_id, event=event, url=url)
        # Re-raise the exception because some of these tasks might retry on the exception
        raise

    else:
        track_response_code(resp.status_code, slug, event)
        buffer.add_request(
            response_code=resp.status_code,
            org_id=org_id,
            event=event,
            url=url,
            error_id=resp.headers.get("Sentry-Hook-Error"),
            project_id=resp.headers.get("Sentry-Hook-Project"),
        )

        if resp.status_code == 503:
            raise ApiHostError.from_request(resp.request)

        elif resp.status_code == 504:
            raise ApiTimeoutError.from_request(resp.request)

        resp.raise_for_status()

        return resp