Fix backend for resolution notes via mobile app (#2117)

# What this PR does
Add first internal api error code and add "resolution_note" param to
resolve endpoint

## Which issue(s) this PR fixes
It fixes resolving alert groups via mobile app when resolution note is
required

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required)
- [ ] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
This commit is contained in:
Innokentii Konstantinov 2023-06-07 20:19:16 +08:00 committed by GitHub
parent 68605029e4
commit a865ae1378
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 5 deletions

18
engine/apps/api/errors.py Normal file
View file

@ -0,0 +1,18 @@
"""errors contains business-logic error codes for internal api.
It's expected that error codes will use 1000-9999 codes range, where first two digits are for entity:
11xx - AlertGroup, 12xx - AlertReceiveChannel, etc.
10xx are saved for non-entity related errors.
"""
# TODO: this package is WIP. It requires validation of code ranges.
from enum import Enum, unique
@unique
class AlertGroupAPIError(Enum):
"""
Error codes for alert group.
Range is 1100-1199
"""
RESOLUTION_NOTE_REQUIRED = 1101

View file

@ -9,6 +9,7 @@ from rest_framework.response import Response
from rest_framework.test import APIClient
from apps.alerts.models import AlertGroup, AlertGroupLogRecord, AlertReceiveChannel
from apps.api.errors import AlertGroupAPIError
from apps.api.permissions import LegacyAccessControlRole
from apps.base.models import UserNotificationPolicyLogRecord
@ -1805,3 +1806,42 @@ def test_direct_paging_integration_treated_as_deleted(
response = client.get(url, format="json", **make_user_auth_headers(user, token))
assert response.json()["alert_receive_channel"]["deleted"] is True
@pytest.mark.django_db
def test_alert_group_resolve_resolution_note(
make_organization_and_user_with_plugin_token,
make_alert_receive_channel,
make_channel_filter,
make_alert_group,
make_alert,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_plugin_token()
alert_receive_channel = make_alert_receive_channel(organization)
channel_filter = make_channel_filter(alert_receive_channel, is_default=True)
new_alert_group = make_alert_group(alert_receive_channel, channel_filter=channel_filter)
make_alert(alert_group=new_alert_group, raw_request_data=alert_raw_request_data)
organization.is_resolution_note_required = True
organization.save()
client = APIClient()
url = reverse("api-internal:alertgroup-resolve", kwargs={"pk": new_alert_group.public_primary_key})
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
with patch(
"apps.alerts.tasks.send_update_resolution_note_signal.send_update_resolution_note_signal.apply_async"
) as mock_signal:
url = reverse("api-internal:alertgroup-resolve", kwargs={"pk": new_alert_group.public_primary_key})
response = client.post(
url, format="json", data={"resolution_note": "hi"}, **make_user_auth_headers(user, token)
)
assert response.status_code == status.HTTP_200_OK
assert new_alert_group.has_resolution_notes
assert mock_signal.called

View file

@ -13,8 +13,10 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from apps.alerts.constants import ActionSource
from apps.alerts.models import Alert, AlertGroup, AlertReceiveChannel, EscalationChain
from apps.alerts.models import Alert, AlertGroup, AlertReceiveChannel, EscalationChain, ResolutionNote
from apps.alerts.paging import unpage_user
from apps.alerts.tasks import send_update_resolution_note_signal
from apps.api.errors import AlertGroupAPIError
from apps.api.permissions import RBACPermission
from apps.api.serializers.alert_group import AlertGroupListSerializer, AlertGroupSerializer
from apps.api.serializers.team import TeamSerializer
@ -456,11 +458,30 @@ class AlertGroupView(
if alert_group.is_maintenance_incident:
alert_group.stop_maintenance(self.request.user)
else:
if organization.is_resolution_note_required and not alert_group.has_resolution_notes:
return Response(
data="Alert group without resolution note cannot be resolved due to organization settings.",
status=status.HTTP_400_BAD_REQUEST,
resolution_note_text = request.data.get("resolution_note")
if resolution_note_text:
rn = ResolutionNote.objects.create(
alert_group=alert_group,
author=self.request.user,
source=ResolutionNote.Source.WEB,
message_text=resolution_note_text[:3000], # trim text to fit in the db field
)
send_update_resolution_note_signal.apply_async(
kwargs={
"alert_group_pk": alert_group.pk,
"resolution_note_pk": rn.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={
"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(self.request.user, action_source=ActionSource.WEB)
return Response(AlertGroupSerializer(alert_group, context={"request": self.request}).data)