oncall-engine/engine/apps/sendgridapp/models.py
Michael Derynck 6b40f95033 World, meet OnCall!
Co-authored-by: Eve832 <eve.meelan@grafana.com>
    Co-authored-by: Francisco Montes de Oca <nevermind89x@gmail.com>
    Co-authored-by: Ildar Iskhakov <ildar.iskhakov@grafana.com>
    Co-authored-by: Innokentii Konstantinov <innokenty.konstantinov@grafana.com>
    Co-authored-by: Julia <ferril.darkdiver@gmail.com>
    Co-authored-by: maskin25 <kengurek@gmail.com>
    Co-authored-by: Matias Bordese <mbordese@gmail.com>
    Co-authored-by: Matvey Kukuy <motakuk@gmail.com>
    Co-authored-by: Michael Derynck <michael.derynck@grafana.com>
    Co-authored-by: Richard Hartmann <richih@richih.org>
    Co-authored-by: Robby Milo <robbymilo@fastmail.com>
    Co-authored-by: Timur Olzhabayev <timur.olzhabayev@grafana.com>
    Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
    Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2022-06-03 08:09:47 -06:00

185 lines
8.5 KiB
Python

import logging
import uuid
from django.apps import apps
from django.db import models
from python_http_client.exceptions import BadRequestsError, ForbiddenError, UnauthorizedError
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import CustomArg, Mail
from apps.alerts.incident_appearance.renderers.email_renderer import AlertGroupEmailRenderer
from apps.alerts.signals import user_notification_action_triggered_signal
from apps.base.utils import live_settings
from apps.sendgridapp.constants import SendgridEmailMessageStatuses
logger = logging.getLogger(__name__)
class EmailMessageManager(models.Manager):
def update_status(self, message_uuid, message_status):
"""The function checks existence of EmailMessage
instance according to message_uuid and updates status on
message_status
Args:
message_uuid (str): uuid of Email message
message_status (str): new status
Returns:
"""
UserNotificationPolicyLogRecord = apps.get_model("base", "UserNotificationPolicyLogRecord")
if message_uuid and message_status:
email_message_qs = self.filter(message_uuid=message_uuid)
status = SendgridEmailMessageStatuses.DETERMINANT.get(message_status)
if email_message_qs.exists() and status:
email_message_qs.update(status=status)
email_message = email_message_qs.first()
log_record = None
if status == SendgridEmailMessageStatuses.DELIVERED:
log_record = UserNotificationPolicyLogRecord(
author=email_message.receiver,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS,
notification_policy=email_message.notification_policy,
alert_group=email_message.represents_alert_group,
notification_step=email_message.notification_policy.step
if email_message.notification_policy
else None,
notification_channel=email_message.notification_policy.notify_by
if email_message.notification_policy
else None,
)
elif status in [
SendgridEmailMessageStatuses.BOUNCE,
SendgridEmailMessageStatuses.BLOCKED,
SendgridEmailMessageStatuses.DROPPED,
]:
log_record = UserNotificationPolicyLogRecord(
author=email_message.receiver,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
notification_policy=email_message.notification_policy,
alert_group=email_message.represents_alert_group,
notification_error_code=email_message.get_error_code_by_sendgrid_status(status),
notification_step=email_message.notification_policy.step
if email_message.notification_policy
else None,
notification_channel=email_message.notification_policy.notify_by
if email_message.notification_policy
else None,
)
if log_record is not None:
log_record.save()
user_notification_action_triggered_signal.send(
sender=EmailMessage.objects.update_status, log_record=log_record
)
class EmailMessage(models.Model):
objects = EmailMessageManager()
message_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
exceeded_limit = models.BooleanField(null=True, default=None)
represents_alert = models.ForeignKey("alerts.Alert", on_delete=models.SET_NULL, null=True, default=None)
represents_alert_group = models.ForeignKey("alerts.AlertGroup", on_delete=models.SET_NULL, null=True, default=None)
notification_policy = models.ForeignKey(
"base.UserNotificationPolicy", on_delete=models.SET_NULL, null=True, default=None
)
receiver = models.ForeignKey("user_management.User", on_delete=models.PROTECT, null=True, default=None)
status = models.PositiveSmallIntegerField(blank=True, null=True, choices=SendgridEmailMessageStatuses.CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
@staticmethod
def send_incident_mail(user, alert_group, notification_policy):
UserNotificationPolicyLogRecord = apps.get_model("base", "UserNotificationPolicyLogRecord")
log_record = None
alert = alert_group.alerts.first()
email_message = EmailMessage(
represents_alert_group=alert_group,
represents_alert=alert,
receiver=user,
notification_policy=notification_policy,
)
emails_left = alert_group.channel.organization.emails_left(user)
if emails_left > 0:
email_message.exceeded_limit = False
limit_notification = False
if emails_left < 5:
limit_notification = True
subject, html_content = AlertGroupEmailRenderer(alert_group).render(limit_notification)
message = Mail(
from_email=live_settings.SENDGRID_FROM_EMAIL,
to_emails=user.email,
subject=subject,
html_content=html_content,
)
custom_arg = CustomArg("message_uuid", str(email_message.message_uuid))
message.add_custom_arg(custom_arg)
sendgrid_client = SendGridAPIClient(live_settings.SENDGRID_API_KEY)
try:
response = sendgrid_client.send(message)
sending_status = True
except (BadRequestsError, UnauthorizedError, ForbiddenError) as e:
logger.error(f"Error email sending: {e}")
sending_status = False
else:
if response.status_code == 202:
email_message.status = SendgridEmailMessageStatuses.ACCEPTED
email_message.save()
else:
logger.error(f"Error email sending: status code: {response.status_code}")
sending_status = False
if not sending_status:
log_record = UserNotificationPolicyLogRecord(
author=user,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
notification_policy=notification_policy,
alert_group=alert_group,
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ABLE_TO_SEND_MAIL,
notification_step=notification_policy.step if notification_policy else None,
notification_channel=notification_policy.notify_by if notification_policy else None,
)
else:
log_record = UserNotificationPolicyLogRecord(
author=user,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
notification_policy=notification_policy,
alert_group=alert_group,
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_MAIL_LIMIT_EXCEEDED,
notification_step=notification_policy.step if notification_policy else None,
notification_channel=notification_policy.notify_by if notification_policy else None,
)
email_message.exceeded_limit = True
email_message.save()
if log_record is not None:
log_record.save()
user_notification_action_triggered_signal.send(
sender=EmailMessage.send_incident_mail, log_record=log_record
)
@staticmethod
def get_error_code_by_sendgrid_status(status):
UserNotificationPolicyLogRecord = apps.get_model("base", "UserNotificationPolicyLogRecord")
SENDGRID_ERRORS_TO_ERROR_CODES_MAP = {
SendgridEmailMessageStatuses.BOUNCE: UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_MAIL_DELIVERY_FAILED,
SendgridEmailMessageStatuses.BLOCKED: UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_MAIL_DELIVERY_FAILED,
SendgridEmailMessageStatuses.DROPPED: UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_MAIL_DELIVERY_FAILED,
}
return SENDGRID_ERRORS_TO_ERROR_CODES_MAP.get(status, None)