oncall-engine/engine/apps/user_management/tests/test_organization.py
Innokentii Konstantinov 1f786e8d2a
Phone provider refactoring (#1713)
# What this PR does
This PR moves phone notification logic into separate object PhoneBackend
and introduces PhoneProvider interface to hide actual implementation of
external phone services provider. It should allow add new phone
providers just by implementing one class (See SimplePhoneProvider for
example).
# Why 
[Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that
our phone notification system is not flexible. However this is one of
the most frequent community questions - how to add "X" phone provider.
Also, this refactoring move us one step closer to unifying all
notification backends, since with PhoneBackend all phone notification
logic is collected in one place and independent from concrete
realisation.
# Highligts
1. PhoneBackend object - contains all phone notifications business
logic.
2. PhoneProvider - interface to  external phone services provider.
3. TwilioPhoneProvider and SimplePhoneProvider - two examples of
PhoneProvider implementation.
4. PhoneCallRecord and SMSRecord models. I introduced these models to
keep phone notification limits logic decoupled from external providers.
Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the
new table to not to reset limits counter. To be able to receive status
callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still
exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to
not to leat twilio logic into core code.

---------

Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 06:27:48 +00:00

200 lines
7.4 KiB
Python

import pytest
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
from django.utils import timezone
from rest_framework.test import APIClient
from apps.alerts.models import AlertGroupLogRecord, AlertReceiveChannel, EscalationPolicy
from apps.base.models import UserNotificationPolicy, UserNotificationPolicyLogRecord
from apps.schedules.models import OnCallScheduleICal, OnCallScheduleWeb
from apps.telegram.models import TelegramMessage
from apps.user_management.models import Organization
@pytest.mark.django_db
def test_organization_soft_delete(
make_organization_and_user_with_token,
make_alert_receive_channel,
):
organization, _, token = make_organization_and_user_with_token()
alert_receive_channel = make_alert_receive_channel(
organization=organization, integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER
)
org_id = organization.id
organization.delete()
deleted_organization = Organization.objects_with_deleted.get(id=org_id)
# check if org soft-deleted
assert deleted_organization.deleted_at is not None
# check if public api responds with 404
client = APIClient()
url = reverse("api-public:integrations-list")
response = client.get(url, format="json", HTTP_AUTHORIZATION=f"{token}")
assert response.status_code == 404
# check if alert receiver view responds with 403
url = reverse("integrations:alertmanager", kwargs={"alert_channel_key": alert_receive_channel.token})
data = {"a": "b"}
response = client.post(url, data, format="json")
assert response.status_code == 403
@pytest.mark.django_db
def test_organization_hard_delete(
make_organization,
make_user,
make_team,
make_slack_team_identity,
make_slack_user_identity,
make_slack_message,
make_slack_action_record,
make_schedule,
make_custom_action,
make_alert_receive_channel,
make_escalation_chain,
make_escalation_policy,
make_channel_filter,
make_user_notification_policy,
make_telegram_user_connector,
make_telegram_channel,
make_telegram_verification_code,
make_telegram_channel_verification_code,
make_telegram_message,
make_alert,
make_alert_group,
make_alert_group_log_record,
make_user_notification_policy_log_record,
make_sms_record,
make_phone_call_record,
make_token_for_organization,
make_public_api_token,
make_invitation,
make_resolution_note,
make_resolution_note_slack_message,
):
slack_team_identity = make_slack_team_identity()
organization = make_organization(slack_team_identity=slack_team_identity)
slack_user_identity_1 = make_slack_user_identity(slack_team_identity=slack_team_identity, slack_id="USER_1")
slack_user_identity_2 = make_slack_user_identity(slack_team_identity=slack_team_identity, slack_id="USER_2")
user_1 = make_user(organization=organization, slack_user_identity=slack_user_identity_1)
user_2 = make_user(organization=organization, slack_user_identity=slack_user_identity_2)
user_notification_policy = make_user_notification_policy(
user=user_1, step=UserNotificationPolicy.Step.WAIT, wait_delay=timezone.timedelta(minutes=15), important=False
)
team = make_team(organization=organization)
team.users.add(user_1)
# Creating different types of schedules to check that deletion works well with PolymorphicModel
schedule_web = make_schedule(organization=organization, schedule_class=OnCallScheduleWeb)
schedule_ical = make_schedule(organization=organization, schedule_class=OnCallScheduleICal)
custom_action = make_custom_action(organization=organization)
escalation_chain = make_escalation_chain(organization=organization)
escalation_policy = make_escalation_policy(
escalation_chain=escalation_chain,
escalation_policy_step=EscalationPolicy.STEP_WAIT,
wait_delay=EscalationPolicy.ONE_MINUTE,
last_notified_user=user_1,
)
escalation_policy.notify_to_users_queue.set([user_1, user_2])
alert_receive_channel = make_alert_receive_channel(organization=organization, author=user_1)
channel_filter = make_channel_filter(alert_receive_channel, is_default=True, escalation_chain=escalation_chain)
alert_group = make_alert_group(
alert_receive_channel=alert_receive_channel,
acknowledged_by_user=user_1,
silenced_by_user=user_2,
wiped_by=user_2,
)
alert = make_alert(alert_group=alert_group, raw_request_data={})
alert_group.resolved_by_alert = alert
alert_group.save(update_fields=["resolved_by_alert"])
user_notification_policy_log_record = make_user_notification_policy_log_record(
author=user_1,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
notification_policy=user_notification_policy,
notification_step=user_notification_policy.step,
notification_channel=user_notification_policy.notify_by,
alert_group=alert_group,
)
sms_record = make_sms_record(receiver=user_1, represents_alert=alert, represents_alert_group=alert_group)
phone_call_record = make_phone_call_record(
receiver=user_1, represents_alert=alert, represents_alert_group=alert_group
)
telegram_user_connector = make_telegram_user_connector(user=user_1)
telegram_channel = make_telegram_channel(organization=organization)
telegram_verification_code = make_telegram_verification_code(user=user_1)
telegram_channel_verification_code = make_telegram_channel_verification_code(
organization=organization, author=user_1
)
telegram_message = make_telegram_message(alert_group=alert_group, message_type=TelegramMessage.ALERT_GROUP_MESSAGE)
slack_message = make_slack_message(alert_group=alert_group)
slack_action_record = make_slack_action_record(organization=organization, user=user_1)
plugin_token, _ = make_token_for_organization(organization)
public_api_token, _ = make_public_api_token(user_1, organization)
invitation = make_invitation(alert_group=alert_group, author=user_1, invitee=user_2)
alert_group_log_record = make_alert_group_log_record(
alert_group=alert_group, author=user_1, type=AlertGroupLogRecord.TYPE_ACK, invitation=invitation
)
resolution_note_slack_message = make_resolution_note_slack_message(
alert_group=alert_group, user=user_1, added_by_user=user_2
)
resolution_note = make_resolution_note(
alert_group=alert_group, author=user_1, resolution_note_slack_message=resolution_note_slack_message
)
cascading_objects = [
user_1,
user_2,
team,
user_notification_policy,
schedule_web,
schedule_ical,
custom_action,
escalation_chain,
escalation_policy,
alert_receive_channel,
channel_filter,
alert_group,
alert,
alert_group_log_record,
user_notification_policy_log_record,
phone_call_record,
sms_record,
telegram_message,
telegram_user_connector,
telegram_channel,
telegram_verification_code,
telegram_channel_verification_code,
slack_message,
slack_action_record,
plugin_token,
public_api_token,
invitation,
resolution_note,
resolution_note_slack_message,
]
organization.hard_delete()
for obj in cascading_objects:
with pytest.raises(ObjectDoesNotExist):
obj.refresh_from_db()