Skip to content

Commit

Permalink
feat(crons): Add delete project processing error by type (#72623)
Browse files Browse the repository at this point in the history
Very similar to #72135

Adds an endpoint to delete monitor processing errors on a project by
type.

I opted to create this as a new endpoint, but if we are against that, I
could see an option where we add it to the existing
`OrganizationMonitorProcessingErrorsIndexEndpoint` and accept a
query_parameter for the project we want the delete to be applied onto.
  • Loading branch information
David Wang authored Jun 20, 2024
1 parent 06da377 commit 834d912
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/sentry/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@
from sentry.monitors.endpoints.project_processing_errors_details import (
ProjectProcessingErrorsDetailsEndpoint,
)
from sentry.monitors.endpoints.project_processing_errors_index import (
ProjectProcessingErrorsIndexEndpoint,
)
from sentry.remote_config.endpoints import (
ProjectConfigurationEndpoint,
ProjectConfigurationProxyEndpoint,
Expand Down Expand Up @@ -2794,6 +2797,11 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
ProjectProcessingErrorsDetailsEndpoint.as_view(),
name="sentry-api-0-project-processing-errors-details",
),
re_path(
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/processing-errors/$",
ProjectProcessingErrorsIndexEndpoint.as_view(),
name="sentry-api-0-project-processing-errors-index",
),
re_path(
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/processing-errors/$",
ProjectMonitorProcessingErrorsIndexEndpoint.as_view(),
Expand Down
52 changes: 52 additions & 0 deletions src/sentry/monitors/endpoints/project_processing_errors_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from __future__ import annotations

from drf_spectacular.utils import extend_schema
from rest_framework.exceptions import ValidationError
from rest_framework.request import Request
from rest_framework.response import Response

from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import region_silo_endpoint
from sentry.api.bases import ProjectEndpoint
from sentry.apidocs.constants import (
RESPONSE_FORBIDDEN,
RESPONSE_NO_CONTENT,
RESPONSE_NOT_FOUND,
RESPONSE_UNAUTHORIZED,
)
from sentry.apidocs.parameters import GlobalParams
from sentry.models.project import Project
from sentry.monitors.processing_errors.errors import ProcessingErrorType
from sentry.monitors.processing_errors.manager import delete_errors_for_project_by_type


@region_silo_endpoint
@extend_schema(tags=["Crons"])
class ProjectProcessingErrorsIndexEndpoint(ProjectEndpoint):
publish_status = {
"DELETE": ApiPublishStatus.PRIVATE,
}
owner = ApiOwner.CRONS

@extend_schema(
operation_id="Delete all processing errors by type for a Project",
parameters=[
GlobalParams.ORG_ID_OR_SLUG,
GlobalParams.PROJECT_ID_OR_SLUG,
],
responses={
204: RESPONSE_NO_CONTENT,
401: RESPONSE_UNAUTHORIZED,
403: RESPONSE_FORBIDDEN,
404: RESPONSE_NOT_FOUND,
},
)
def delete(self, request: Request, project: Project) -> Response:
try:
error_type = ProcessingErrorType(int(request.GET.get("errortype", -1)))
except ValueError:
raise ValidationError("Invalid error type")

delete_errors_for_project_by_type(project, error_type)
return self.respond(status=204)
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from sentry.monitors.processing_errors.errors import ProcessingErrorType
from sentry.monitors.processing_errors.manager import get_errors_for_projects, store_error
from sentry.monitors.testutils import build_checkin_processing_error
from sentry.testutils.cases import APITestCase, MonitorTestCase


class ProjectProcessingErrorsIndexEndpointTest(MonitorTestCase, APITestCase):
endpoint = "sentry-api-0-project-processing-errors-index"
method = "delete"

def setUp(self):
super().setUp()
self.login_as(user=self.user)

def test_no_error_type(self):
resp = self.get_error_response(self.organization.slug, self.project.slug)
assert resp.status_code == 400
assert resp.content == b'["Invalid error type"]'

def test_invalid_error_type(self):
resp = self.get_error_response(
self.organization.slug, self.project.slug, qs_params={"errortype": "17"}
)
assert resp.status_code == 400
assert resp.content == b'["Invalid error type"]'

def test(self):
monitor_errors = [
build_checkin_processing_error(
[{"type": ProcessingErrorType.CHECKIN_INVALID_GUID}],
message_overrides={"project_id": self.project.id},
),
build_checkin_processing_error(
[{"type": ProcessingErrorType.CHECKIN_INVALID_GUID}],
message_overrides={"project_id": self.project.id},
),
build_checkin_processing_error(
[{"type": ProcessingErrorType.CHECKIN_INVALID_GUID}],
message_overrides={"project_id": self.project.id},
),
build_checkin_processing_error(
[{"type": ProcessingErrorType.CHECKIN_INVALID_GUID}],
message_overrides={"project_id": self.project.id},
),
build_checkin_processing_error(
[{"type": ProcessingErrorType.CHECKIN_INVALID_DURATION, "duration": "-1"}],
message_overrides={"project_id": self.project.id},
),
]

for error in monitor_errors:
store_error(error, None)

resp = self.get_success_response(
self.organization.slug,
self.project.slug,
qs_params={"errortype": "4"},
)
assert resp.status_code == 204
assert get_errors_for_projects([self.project]) == [monitor_errors[4]]

0 comments on commit 834d912

Please sign in to comment.