Skip to content

Commit

Permalink
Recommended changes to PR to support adding notify to team
Browse files Browse the repository at this point in the history
  • Loading branch information
xssfox committed Feb 14, 2024
1 parent 8ad74aa commit 67e3294
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ def __init__(
custom_webhook,
notify_schedule,
notify_to_group,
notify_to_team_members,
escalation_counter,
passed_last_time,
pause_escalation,
notify_to_team_members=None,
):
self.id = id
self.order = order
Expand Down Expand Up @@ -385,6 +385,7 @@ def _escalation_step_notify_team_members(self, alert_group: "AlertGroup", reason
),
kwargs={
"reason": reason,
"important": self.step == EscalationPolicy.STEP_NOTIFY_TEAM_MEMBERS_IMPORTANT,
},
immutable=True,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class Migration(migrations.Migration):

dependencies = [
('alerts', '0041_alertreceivechannel_unique_direct_paging_integration_per_team'),
('alerts', '0044_alertreceivechannel_alertmanager_v2_backup_templates_and_more'),
]

operations = [
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/alerts/models/alert_group_log_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ def rendered_log_line_action(self, for_slack=False, html=False, substitute_autho
elif self.escalation_error_code == AlertGroupLogRecord.ERROR_ESCALATION_NOTIFY_GROUP_STEP_IS_NOT_CONFIGURED:
result += 'skipped escalation step "Notify Group" because it is not configured'
elif self.escalation_error_code == AlertGroupLogRecord.ERROR_ESCALATION_NOTIFY_TEAM_MEMBERS_STEP_IS_NOT_CONFIGURED:
result += 'skipped escalation step "Notify Team" because it is not configured'
result += 'skipped escalation step "Notify Team Members" because it is not configured'
elif (
self.escalation_error_code
== AlertGroupLogRecord.ERROR_ESCALATION_TRIGGER_CUSTOM_BUTTON_STEP_IS_NOT_CONFIGURED
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/alerts/models/escalation_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class EscalationPolicy(OrderedModel):
# Common steps
STEP_WAIT: ("Wait {{wait_delay}} minute(s)", "Wait"),
STEP_NOTIFY_MULTIPLE_USERS: ("Start {{importance}} notification for {{users}}", "Notify users"),
STEP_NOTIFY_TEAM_MEMBERS: ("Start {{importance}} notification for team members {{team}}", "Notify all team members"),
STEP_NOTIFY_TEAM_MEMBERS: ("Start {{importance}} notification for {{team}} team members", "Notify all team members"),
STEP_NOTIFY_SCHEDULE: (
"Start {{importance}} notification for schedule {{schedule}}",
"Notify users from on-call schedule",
Expand Down
33 changes: 17 additions & 16 deletions engine/apps/alerts/tasks/notify_team_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,27 @@
def notify_team_members_task(
team_pk,
alert_group_pk,
previous_notification_policy_pk=None,
reason=None,
prevent_posting_to_thread=False,
notify_even_acknowledged=False,
important=False,
notify_anyway=False,
**kwargs # kwargs to pass through to notify_user_task.apply_async
):
from apps.user_management.models import Team


with transaction.atomic():
try:
team = Team.objects.filter(pk=team_pk).first()
except Team.DoesNotExist:
return f"notify_team_members_task: team {team_pk} doesn't exist"
try:
team = Team.objects.filter(pk=team_pk).first()
except Team.DoesNotExist:
return f"notify_team_members_task: team {team_pk} doesn't exist"


for user in team.users.all():
try:
for user in team.users.all():
try:
if user.is_notification_allowed:
task_logger.debug(f"notify_team_members_task: notifying {user.pk}")
notify_user_task(user.pk, alert_group_pk, previous_notification_policy_pk, reason, prevent_posting_to_thread, notify_even_acknowledged, important, notify_anyway)
except:
task_logger.info(f"notify_team_members_task: user {user.pk} failed")
notify_user_task.apply_async(
args=(
user.pk,
alert_group_pk,
),
kwargs=kwargs
)
except:
task_logger.info(f"notify_team_members_task: user {user.pk} failed")
60 changes: 60 additions & 0 deletions engine/apps/alerts/tests/test_escalation_snapshot_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,66 @@ def test_deserialize_escalation_snapshot(
assert deserialized_escalation_snapshot.stop_escalation is False


@pytest.mark.django_db
def test_deserialize_escalation_snapshot_missing_notify_to_team_members(
make_organization_and_user,
make_alert_receive_channel,
make_channel_filter,
make_escalation_chain,
make_escalation_policy,
make_alert_group,
):

organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
escalation_chain = make_escalation_chain(organization=organization)
channel_filter = make_channel_filter(alert_receive_channel, escalation_chain=escalation_chain)
escalation_policy = make_escalation_policy(
escalation_chain=channel_filter.escalation_chain,
escalation_policy_step=EscalationPolicy.STEP_WAIT,
wait_delay=EscalationPolicy.FIFTEEN_MINUTES,
)

alert_group = make_alert_group(alert_receive_channel, channel_filter=channel_filter)
alert_group.raw_escalation_snapshot = alert_group.build_raw_escalation_snapshot()
del alert_group.raw_escalation_snapshot['escalation_policies_snapshots'][0]['notify_to_team_members']

deserialized_escalation_snapshot = alert_group._deserialize_escalation_snapshot(alert_group.raw_escalation_snapshot)
assert deserialized_escalation_snapshot.escalation_policies_snapshots[0].notify_to_team_members == None

@patch("apps.alerts.models.alert_group.AlertGroup.slack_channel_id", new_callable=PropertyMock)
@pytest.mark.django_db
def test_deserialize_escalation_snapshot_notify_to_team_members(
mock_alert_group_slack_channel_id,
make_organization_and_user,
make_alert_receive_channel,
make_channel_filter,
make_escalation_chain,
make_escalation_policy,
make_alert_group,
make_team
):
mock_alert_group_slack_channel_id.return_value = MOCK_SLACK_CHANNEL_ID

organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
escalation_chain = make_escalation_chain(organization=organization)
channel_filter = make_channel_filter(alert_receive_channel, escalation_chain=escalation_chain)
team = make_team(organization)
escalation_policy = make_escalation_policy(
escalation_chain=channel_filter.escalation_chain,
escalation_policy_step=EscalationPolicy.STEP_NOTIFY_TEAM_MEMBERS,
notify_to_team_members = team
)

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.raw_escalation_snapshot['escalation_policies_snapshots'][0]['notify_to_team_members']

deserialized_escalation_snapshot = alert_group._deserialize_escalation_snapshot(alert_group.raw_escalation_snapshot)
assert deserialized_escalation_snapshot.escalation_policies_snapshots[0].notify_to_team_members.id == team.id


@pytest.mark.django_db
def test_escalation_chain_exists(
make_organization_and_user,
Expand Down
38 changes: 32 additions & 6 deletions engine/apps/alerts/tests/test_notify_team_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ def test_notify_team_members(
):
organization = make_organization()
user_1 = make_user(
organization=organization, role=LegacyAccessControlRole.VIEWER, _verified_phone_number="1234567890"
organization=organization, role=LegacyAccessControlRole.ADMIN, _verified_phone_number="1234567890"
)
user_2 = make_user(
organization=organization, role=LegacyAccessControlRole.VIEWER, _verified_phone_number="1234567890"
organization=organization, role=LegacyAccessControlRole.ADMIN, _verified_phone_number="1234567890"
)
user_3 = make_user(
organization=organization, role=LegacyAccessControlRole.VIEWER, _verified_phone_number="1234567890"
organization=organization, role=LegacyAccessControlRole.ADMIN, _verified_phone_number="1234567890"
)
team_1 = make_team(
organization=organization,
Expand All @@ -31,10 +31,36 @@ def test_notify_team_members(
team_1.users.add(user_2)
alert_receive_channel = make_alert_receive_channel(organization=organization)
alert_group = make_alert_group(alert_receive_channel=alert_receive_channel)

with patch("apps.alerts.tasks.notify_team_members.notify_user_task") as mock_execute:
notify_team_members_task(team_1.pk, alert_group.pk)

assert mock_execute.call_args_list[0] == call(user_1.pk, alert_group.pk, None, None, False, False, False, False)
assert mock_execute.call_args_list[1] == call(user_2.pk, alert_group.pk, None, None, False, False, False, False)
assert mock_execute.call_count == 2
assert mock_execute.apply_async.call_args_list[0] == call(args=(user_1.pk, alert_group.pk), kwargs={})
assert mock_execute.apply_async.call_args_list[1] == call(args=(user_2.pk, alert_group.pk), kwargs={})
assert mock_execute.apply_async.call_count == 2


@pytest.mark.django_db
def test_notify_team_members_important(
make_organization,
make_user,
make_alert_receive_channel,
make_alert_group,
make_team
):
organization = make_organization()
user_1 = make_user(
organization=organization, role=LegacyAccessControlRole.ADMIN, _verified_phone_number="1234567890"
)
team_1 = make_team(
organization=organization,
)
team_1.users.add(user_1)
alert_receive_channel = make_alert_receive_channel(organization=organization)
alert_group = make_alert_group(alert_receive_channel=alert_receive_channel)

with patch("apps.alerts.tasks.notify_team_members.notify_user_task") as mock_execute:
notify_team_members_task(team_1.pk, alert_group.pk, important=True)

assert mock_execute.apply_async.call_args_list[0] == call(args=(user_1.pk, alert_group.pk), kwargs={"important": True})
assert mock_execute.apply_async.call_count == 1
27 changes: 27 additions & 0 deletions engine/apps/public_api/tests/test_escalation_policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,30 @@ def test_update_escalation_policy_from_and_to_time(
assert response.data == serializer.data
else:
assert response.json()[field][0] == "Time has wrong format. Use one of these formats instead: hh:mm:ssZ."

@pytest.mark.django_db
def test_create_escalation_policy_using_notify_team_members(
make_organization_and_user_with_token,
make_team,
escalation_policies_setup,
):
organization, user, token = make_organization_and_user_with_token()
escalation_chain, _, _ = escalation_policies_setup(organization, user)
team = make_team(organization)

data_for_create = {
"escalation_chain_id": escalation_chain.public_primary_key,
"type": "notify_team_members",
"position": 0,
"notify_to_team_members": team.team_id
}

client = APIClient()
url = reverse("api-public:escalation_policies-list")
response = client.post(url, data=data_for_create, format="json", HTTP_AUTHORIZATION=token)

assert response.status_code == status.HTTP_201_CREATED

escalation_policy = EscalationPolicy.objects.get(public_primary_key=response.data["id"])
serializer = EscalationPolicySerializer(escalation_policy)
assert response.data == serializer.data
38 changes: 0 additions & 38 deletions grafana-plugin/src/models/escalation_policy.ts

This file was deleted.

0 comments on commit 67e3294

Please sign in to comment.