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:
parent
d5461866d1
commit
ccae9d86b3
4 changed files with 93 additions and 9 deletions
|
|
@ -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')]),
|
||||
),
|
||||
]
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue