Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Raise exceptions instead of returning 400 and 500 requests #5225

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def extra_deps():
return grafana_deps


allow_k8s_contexts(["kind-kind"])
allow_k8s_contexts(["kind-kind", "orbstack"])

# Build the image including frontend folder for pytest
docker_build_sub(
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/api/tests/test_alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -2149,7 +2149,7 @@ def test_alert_group_resolve_resolution_note(
response = client.post(url, format="json", **make_user_auth_headers(user, token))
# check that resolution note is required
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["code"] == AlertGroupAPIError.RESOLUTION_NOTE_REQUIRED.value
assert response.json()["code"] == str(AlertGroupAPIError.RESOLUTION_NOTE_REQUIRED.value)

with patch(
"apps.alerts.tasks.send_update_resolution_note_signal.send_update_resolution_note_signal.apply_async"
Expand Down
24 changes: 10 additions & 14 deletions engine/apps/api/views/alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from apps.labels.utils import is_labels_feature_enabled
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication
from apps.user_management.models import Team, User
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.exceptions import BadRequest, Forbidden
from common.api_helpers.filters import (
NO_TEAM_VALUE,
DateRangeFilterMixin,
Expand Down Expand Up @@ -210,12 +210,9 @@ def retrieve(self, request, *args, **kwargs):
if obj_team is None:
obj_team = Team(public_primary_key=None, name="General", email=None, avatar_url=None)

return Response(
data={"error_code": "wrong_team", "owner_team": TeamSerializer(obj_team).data},
status=status.HTTP_403_FORBIDDEN,
)
raise Forbidden(detail={"error_code": "wrong_team", "owner_team": TeamSerializer(obj_team).data})

return Response(data={"error_code": "wrong_team"}, status=status.HTTP_403_FORBIDDEN)
raise Forbidden(detail={"error_code": "wrong_team"})


class AlertGroupSearchFilter(SearchFilter):
Expand Down Expand Up @@ -509,12 +506,11 @@ def resolve(self, request, pk):
else:
# Check resolution note required setting only if resolution_note_text was not provided.
if organization.is_resolution_note_required and not alert_group.has_resolution_notes:
return Response(
data={
raise BadRequest(
detail={
"code": AlertGroupAPIError.RESOLUTION_NOTE_REQUIRED.value,
"detail": "Alert group without resolution note cannot be resolved due to organization settings",
},
status=status.HTTP_400_BAD_REQUEST,
}
)
alert_group.resolve_by_user_or_backsync(self.request.user, action_source=ActionSource.WEB)
return Response(AlertGroupSerializer(alert_group, context={"request": self.request}).data)
Expand Down Expand Up @@ -558,11 +554,11 @@ def attach(self, request, pk=None):
try:
root_alert_group = self.get_queryset().get(public_primary_key=request.data["root_alert_group_pk"])
except AlertGroup.DoesNotExist:
return Response(status=status.HTTP_400_BAD_REQUEST)
raise BadRequest()
if root_alert_group.resolved or root_alert_group.root_alert_group is not None:
return Response(status=status.HTTP_400_BAD_REQUEST)
raise BadRequest()
if root_alert_group == alert_group:
return Response(status=status.HTTP_400_BAD_REQUEST)
raise BadRequest()

alert_group.attach_by_user(self.request.user, root_alert_group, action_source=ActionSource.WEB)
return Response(AlertGroupSerializer(alert_group, context={"request": self.request}).data)
Expand Down Expand Up @@ -832,7 +828,7 @@ def bulk_action(self, request):
kwargs = {}

if action_name not in AlertGroup.BULK_ACTIONS:
return Response("Unknown action", status=status.HTTP_400_BAD_REQUEST)
raise BadRequest(detail="Unknown action")

if action_name == AlertGroup.SILENCE:
if delay is None:
Expand Down
4 changes: 2 additions & 2 deletions engine/apps/api/views/alert_receive_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from apps.integrations.legacy_prefix import has_legacy_prefix, remove_legacy_prefix
from apps.labels.utils import is_labels_feature_enabled
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.exceptions import BadRequest, Conflict
from common.api_helpers.filters import NO_TEAM_VALUE, ByTeamModelFieldFilterMixin, TeamModelMultipleChoiceFilter
from common.api_helpers.mixins import (
CreateSerializerMixin,
Expand Down Expand Up @@ -611,7 +611,7 @@ def validate_name(self, request):
organization = self.request.auth.organization
name_used = AlertReceiveChannel.objects.filter(organization=organization, verbal_name=verbal_name).exists()
if name_used:
r = Response(status=status.HTTP_409_CONFLICT)
raise Conflict()
else:
r = Response(status=status.HTTP_200_OK)

Expand Down
6 changes: 3 additions & 3 deletions engine/apps/api/views/alert_receive_channel_template.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from rest_framework import mixins, status, viewsets
from rest_framework import mixins, viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from apps.alerts.models import AlertReceiveChannel
from apps.api.permissions import RBACPermission
from apps.api.serializers.alert_receive_channel import AlertReceiveChannelTemplatesSerializer
from apps.auth_token.auth import PluginAuthentication
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.mixins import PublicPrimaryKeyMixin, TeamFilteringMixin
from common.insight_log import EntityEvent, write_resource_insight_log
from common.jinja_templater.apply_jinja_template import JinjaTemplateError
Expand Down Expand Up @@ -47,7 +47,7 @@ def update(self, request, *args, **kwargs):
try:
result = super().update(request, *args, **kwargs)
except JinjaTemplateError as e:
return Response(e.fallback_message, status.HTTP_400_BAD_REQUEST)
raise BadRequest(e.fallback_message)
instance = self.get_object()
new_state = instance.insight_logs_serialized
write_resource_insight_log(
Expand Down
6 changes: 3 additions & 3 deletions engine/apps/api/views/alerts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from rest_framework import status
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
Expand All @@ -16,8 +16,8 @@ def get(self, request, id):
try:
alert = Alert.objects.get(public_primary_key=id)
except Alert.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
raise NotFound()
if alert.group.channel.organization != request.auth.organization:
return Response(status=status.HTTP_404_NOT_FOUND)
raise NotFound()
serializer = AlertRawSerializer(alert)
return Response(serializer.data)
3 changes: 2 additions & 1 deletion engine/apps/api/views/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from apps.grafana_plugin.ui_url_builder import UIURLBuilder
from apps.slack.installation import install_slack_integration
from apps.social_auth.backends import SLACK_INSTALLATION_BACKEND, LoginSlackOAuth2V2
from common.api_helpers.exceptions import InternalServerError

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -59,7 +60,7 @@ def overridden_login_social_auth(request: Request, backend: str) -> Response:
return Response("slack integration installed", 201)
except Exception as e:
logger.exception("overridden_login_social_auth: Failed to install slack via chatops-proxy: %s", e)
return Response({"error": "something went wrong, try again later"}, 500)
raise InternalServerError("Something went wrong, try again later")
else:
# Otherwise use social-auth.
url_to_redirect_to = do_auth(request.backend, redirect_name=REDIRECT_FIELD_NAME).url
Expand Down
6 changes: 3 additions & 3 deletions engine/apps/api/views/escalation_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django_filters import rest_framework as filters
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema, extend_schema_view, inline_serializer
from emoji import emojize
from rest_framework import serializers, status, viewsets
from rest_framework import serializers, viewsets
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated
Expand All @@ -18,7 +18,7 @@
from apps.auth_token.auth import PluginAuthentication
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication
from apps.user_management.models import Team
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.exceptions import BadRequest, Forbidden
from common.api_helpers.filters import (
NO_TEAM_VALUE,
ByTeamModelFieldFilterMixin,
Expand Down Expand Up @@ -164,7 +164,7 @@ def copy(self, request, pk):
try:
team = request.user.available_teams.get(public_primary_key=team_id) if team_id else None
except Team.DoesNotExist:
return Response(data={"error_code": "wrong_team"}, status=status.HTTP_403_FORBIDDEN)
raise Forbidden(detail={"error_code": "wrong_team"})
copy = obj.make_copy(name, team)
serializer = self.get_serializer(copy)
write_resource_insight_log(
Expand Down
2 changes: 2 additions & 0 deletions engine/apps/api/views/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ def get_keys(self, request):
"""List of labels keys"""
organization = self.request.auth.organization
keys, response = LabelsAPIClient(organization.grafana_url, organization.api_token).get_keys()
# TODO: Change all error responses here and below in the file to raise exceptions
# LabelsRepoAPIException should be raised as drf APIException, so we can handle them in handle_exception
return Response(keys, status=response.status_code)

@extend_schema(responses=LabelOptionSerializer)
Expand Down
6 changes: 3 additions & 3 deletions engine/apps/api/views/live_setting.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from contextlib import suppress

from django.conf import settings
from django.http import HttpResponse
from rest_framework import status, viewsets
from rest_framework import viewsets
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAuthenticated
from telegram import error

Expand Down Expand Up @@ -32,7 +32,7 @@ class LiveSettingViewSet(PublicPrimaryKeyMixin[LiveSetting], viewsets.ModelViewS

def dispatch(self, request, *args, **kwargs):
if not settings.FEATURE_LIVE_SETTINGS_ENABLED:
return HttpResponse(status=status.HTTP_404_NOT_FOUND)
raise NotFound()

return super().dispatch(request, *args, **kwargs)

Expand Down
3 changes: 1 addition & 2 deletions engine/apps/api/views/on_call_shifts.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ def preview(self, request):
user_tz, starting_date, days = get_date_range_from_request(self.request)

serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.is_valid(raise_exception=True)

validated_data = serializer._correct_validated_data(
serializer.validated_data["type"], serializer.validated_data
Expand Down
3 changes: 2 additions & 1 deletion engine/apps/api/views/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from apps.base.messaging import get_messaging_backend_from_id
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication
from apps.telegram.client import TelegramClient
from common.api_helpers.exceptions import BadRequest
from common.insight_log import EntityEvent, write_resource_insight_log


Expand Down Expand Up @@ -102,7 +103,7 @@ def get(self, request):
backend_id = request.query_params.get("backend")
backend = get_messaging_backend_from_id(backend_id)
if backend is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
raise BadRequest()

code = backend.generate_channel_verification_code(organization)
return Response(code)
Expand Down
Loading
Loading