From 2c6ef88084597a84a424570e1aa3b6ca2f682a88 Mon Sep 17 00:00:00 2001
From: Julia <ferril.darkdiver@gmail.com>
Date: Mon, 4 Sep 2023 12:34:26 +0200
Subject: [PATCH 1/4] Fix building escalation snapshot if last notified user
 was deleted

---
 .../serializers/escalation_policy_snapshot.py                    | 1 +
 1 file changed, 1 insertion(+)

diff --git a/engine/apps/alerts/escalation_snapshot/serializers/escalation_policy_snapshot.py b/engine/apps/alerts/escalation_snapshot/serializers/escalation_policy_snapshot.py
index d097833e40..6d648af2bf 100644
--- a/engine/apps/alerts/escalation_snapshot/serializers/escalation_policy_snapshot.py
+++ b/engine/apps/alerts/escalation_snapshot/serializers/escalation_policy_snapshot.py
@@ -64,6 +64,7 @@ class EscalationPolicySnapshotSerializer(serializers.ModelSerializer):
     num_alerts_in_window = serializers.IntegerField(allow_null=True, default=None)
     num_minutes_in_window = serializers.IntegerField(allow_null=True, default=None)
     pause_escalation = serializers.BooleanField(default=False)
+    last_notified_user = PrimaryKeyRelatedFieldWithNoneValue(allow_null=True, queryset=User.objects)
 
     class Meta:
         model = EscalationPolicy

From 9ead8302a45e3d4e13d638783c60ee4c6c859495 Mon Sep 17 00:00:00 2001
From: Julia <ferril.darkdiver@gmail.com>
Date: Mon, 4 Sep 2023 12:35:03 +0200
Subject: [PATCH 2/4] Add test

---
 .../alerts/tests/test_escalation_snapshot.py  | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/engine/apps/alerts/tests/test_escalation_snapshot.py b/engine/apps/alerts/tests/test_escalation_snapshot.py
index 66003eebca..da0b630483 100644
--- a/engine/apps/alerts/tests/test_escalation_snapshot.py
+++ b/engine/apps/alerts/tests/test_escalation_snapshot.py
@@ -266,3 +266,55 @@ def test_escalation_snapshot_non_sequential_orders(
 
     policy_ids = [p.id for p in escalation_snapshot.executed_escalation_policy_snapshots]
     assert policy_ids == [step_1.id, step_2.id]
+
+
+@pytest.mark.django_db
+def test_serialize_escalation_snapshot_with_deleted_user(
+    make_organization_and_user,
+    make_user_for_organization,
+    make_alert_receive_channel,
+    make_channel_filter,
+    make_escalation_chain,
+    make_escalation_policy,
+    make_alert_group,
+):
+    organization, user = make_organization_and_user()
+    alert_receive_channel = make_alert_receive_channel(organization)
+    escalation_chain = make_escalation_chain(organization)
+    channel_filter = make_channel_filter(
+        alert_receive_channel,
+        escalation_chain=escalation_chain,
+        notification_backends={"BACKEND": {"channel_id": "abc123"}},
+    )
+
+    notify_users_queue = make_escalation_policy(
+        escalation_chain=channel_filter.escalation_chain,
+        escalation_policy_step=EscalationPolicy.STEP_NOTIFY_USERS_QUEUE,
+        last_notified_user=user,
+    )
+    notify_users_queue.notify_to_users_queue.set([user])
+
+    alert_group = make_alert_group(alert_receive_channel, channel_filter=channel_filter)
+    alert_group.raw_escalation_snapshot = alert_group.build_raw_escalation_snapshot()
+    alert_group.save()
+    escalation_snapshot = alert_group.escalation_snapshot
+
+    assert notify_users_queue.last_notified_user == user
+    assert escalation_snapshot.escalation_policies_snapshots[0].last_notified_user == user
+    assert len(escalation_snapshot.escalation_policies_snapshots[0].notify_to_users_queue) == 1
+
+    # delete user
+    user.is_active = None
+    user.save()
+
+    alert_group.raw_escalation_snapshot = alert_group.build_raw_escalation_snapshot()
+    # clear cached_property
+    del alert_group.escalation_snapshot
+    alert_group.save()
+
+    escalation_snapshot = alert_group.escalation_snapshot
+
+    assert notify_users_queue.last_notified_user == user
+    assert escalation_snapshot is not None
+    assert escalation_snapshot.escalation_policies_snapshots[0].last_notified_user is None
+    assert len(escalation_snapshot.escalation_policies_snapshots[0].notify_to_users_queue) == 0

From c635200a51a16ac36c8fb61cfb93bfd66fa28ef9 Mon Sep 17 00:00:00 2001
From: Julia <ferril.darkdiver@gmail.com>
Date: Mon, 4 Sep 2023 12:40:05 +0200
Subject: [PATCH 3/4] update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9c322c8e3..5a648c22c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Fixed
 
 - Fix for Cloud plugin install not refreshing page after completion ([2974](https://github.com/grafana/oncall/issues/2874))
+- Fix escalation snapshot building if user was deleted @Ferril ([#2954](https://github.com/grafana/oncall/pull/2954))
 
 ## v1.3.30 (2023-08-31)
 

From 007d743bb060901ade8d304b66aa443565eb9aa6 Mon Sep 17 00:00:00 2001
From: Julia <ferril.darkdiver@gmail.com>
Date: Mon, 4 Sep 2023 12:57:49 +0200
Subject: [PATCH 4/4] Fix test

---
 engine/apps/api/tests/test_escalation_policy.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/engine/apps/api/tests/test_escalation_policy.py b/engine/apps/api/tests/test_escalation_policy.py
index bbb753294b..f14d4daaca 100644
--- a/engine/apps/api/tests/test_escalation_policy.py
+++ b/engine/apps/api/tests/test_escalation_policy.py
@@ -846,6 +846,9 @@ def test_escalation_policy_filter_by_user(
 
     assert response.status_code == status.HTTP_200_OK
 
+    result = response.json()
+    assert set(result[1]["notify_to_users_queue"]) == {user.public_primary_key, second_user.public_primary_key}
+    expected_payload[1]["notify_to_users_queue"] = result[1]["notify_to_users_queue"]
     assert response.json() == expected_payload