Add an ability to use an escalation chain for direct paging (#1161)

# What this PR does
Adds an ability to page an escalation chain for a newly created direct
paging alert group using the internal API. Also [adds a forgotten
migration](32fc44e744)
related to the direct paging backend.
Related to https://github.com/grafana/oncall/issues/823

## Checklist

- [x] Tests updated
- [ ] Documentation added (N/A)
- [ ] `CHANGELOG.md` updated (N/A)
This commit is contained in:
Vadim Stepanov 2023-01-19 18:51:57 +00:00 committed by GitHub
parent d5461866d1
commit ccae9d86b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 9 deletions

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-01-19 18:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('alerts', '0007_populate_web_title_cache'),
]
operations = [
migrations.AlterField(
model_name='alertgrouplogrecord',
name='type',
field=models.IntegerField(choices=[(0, 'Acknowledged'), (1, 'Unacknowledged'), (2, 'Invite'), (3, 'Stop invitation'), (4, 'Re-invite'), (5, 'Escalation triggered'), (6, 'Invitation triggered'), (16, 'Escalation finished'), (7, 'Silenced'), (15, 'Unsilenced'), (8, 'Attached'), (9, 'Unattached'), (10, 'Custom button triggered'), (11, 'Unacknowledged by timeout'), (12, 'Failed attachment'), (13, 'Incident resolved'), (14, 'Incident unresolved'), (17, 'Escalation failed'), (18, 'Acknowledge reminder triggered'), (19, 'Wiped'), (20, 'Deleted'), (21, 'Incident registered'), (22, 'A route is assigned to the incident'), (23, 'Trigger direct paging escalation'), (24, 'Unpage a user')]),
),
]

View file

@ -40,6 +40,9 @@ class DirectPagingSerializer(serializers.Serializer):
users = UserReferenceSerializer(many=True, required=False, default=list)
schedules = ScheduleReferenceSerializer(many=True, required=False, default=list)
escalation_chain_id = serializers.CharField(required=False, default=None)
escalation_chain = serializers.HiddenField(default=None) # set in DirectPagingSerializer.validate
alert_group_id = serializers.CharField(required=False, default=None)
alert_group = serializers.HiddenField(default=None) # set in DirectPagingSerializer.validate
@ -47,19 +50,37 @@ class DirectPagingSerializer(serializers.Serializer):
message = serializers.CharField(required=False, default=None)
def validate(self, attrs):
if len(attrs["users"]) == 0 and len(attrs["schedules"]) == 0:
raise serializers.ValidationError("Provide at least one user or schedule")
organization = self.context["organization"]
if attrs["alert_group_id"] and (attrs["title"] or attrs["message"]):
users = attrs["users"]
schedules = attrs["schedules"]
escalation_chain_id = attrs["escalation_chain_id"]
alert_group_id = attrs["alert_group_id"]
title = attrs["title"]
message = attrs["message"]
if len(users) == 0 and len(schedules) == 0 and not escalation_chain_id:
raise serializers.ValidationError("Provide users, schedules, or an escalation chain")
if alert_group_id and (title or message):
raise serializers.ValidationError("alert_group_id and (title, message) are mutually exclusive")
if attrs["alert_group_id"]:
organization = self.context["organization"]
if alert_group_id and escalation_chain_id:
raise serializers.ValidationError("escalation_chain_id is not supported for existing alert groups")
if alert_group_id:
try:
attrs["alert_group"] = AlertGroup.unarchived_objects.get(
public_primary_key=attrs["alert_group_id"], channel__organization=organization
public_primary_key=alert_group_id, channel__organization=organization
)
except ObjectDoesNotExist:
raise serializers.ValidationError("Alert group {} does not exist".format(attrs["alert_group_id"]))
raise serializers.ValidationError("Alert group {} does not exist".format(alert_group_id))
if escalation_chain_id:
try:
attrs["escalation_chain"] = organization.escalation_chains.get(public_primary_key=escalation_chain_id)
except ObjectDoesNotExist:
raise serializers.ValidationError("Escalation chain {} does not exist".format(escalation_chain_id))
return attrs

View file

@ -9,7 +9,11 @@ from apps.schedules.models import OnCallScheduleCalendar, OnCallScheduleICal
@pytest.mark.django_db
def test_direct_paging_new_alert_group(
make_organization_and_user_with_plugin_token, make_user, make_schedule, make_user_auth_headers
make_organization_and_user_with_plugin_token,
make_user,
make_schedule,
make_escalation_chain,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_plugin_token(role=LegacyAccessControlRole.EDITOR)
@ -32,6 +36,8 @@ def test_direct_paging_new_alert_group(
},
]
escalation_chain_to_page = make_escalation_chain(organization)
title = "Test Alert Group"
message = "Testing direct paging with new alert group"
@ -40,7 +46,13 @@ def test_direct_paging_new_alert_group(
response = client.post(
url,
data={"users": users_to_page, "schedules": schedules_to_page, "title": title, "message": message},
data={
"users": users_to_page,
"schedules": schedules_to_page,
"escalation_chain_id": escalation_chain_to_page.public_primary_key,
"title": title,
"message": message,
},
format="json",
**make_user_auth_headers(user, token),
)
@ -94,6 +106,38 @@ def test_direct_paging_existing_alert_group(
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_direct_paging_existing_alert_group_and_escalation_chain(
make_organization_and_user_with_plugin_token,
make_user,
make_schedule,
make_escalation_chain,
make_alert_receive_channel,
make_alert_group,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_plugin_token(role=LegacyAccessControlRole.EDITOR)
escalation_chain_to_page = make_escalation_chain(organization)
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
client = APIClient()
url = reverse("api-internal:direct_paging")
response = client.post(
url,
data={
"escalation_chain_id": escalation_chain_to_page.public_primary_key,
"alert_group_id": alert_group.public_primary_key,
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_direct_paging_no_title(
make_organization_and_user_with_plugin_token,

View file

@ -38,6 +38,7 @@ class DirectPagingAPIView(APIView):
message=serializer.validated_data["message"],
users=users,
schedules=schedules,
escalation_chain=serializer.validated_data["escalation_chain"],
alert_group=serializer.validated_data["alert_group"],
)