ovh: send email credit alerts (#42921)

This commit is contained in:
Valentin Deniaud 2020-10-07 11:31:03 +02:00
parent 153a91373d
commit fc615aff90
11 changed files with 198 additions and 11 deletions

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-10-26 13:24
from __future__ import unicode_literals
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ovh', '0010_auto_20201008_1126'),
]
operations = [
migrations.AddField(
model_name='ovhsmsgateway',
name='alert_emails',
field=django.contrib.postgres.fields.ArrayField(base_field=models.EmailField(blank=True, max_length=254), blank=True, null=True, size=None, verbose_name='Email addresses to send credit alerts to'),
),
migrations.AddField(
model_name='ovhsmsgateway',
name='credit_alert_timestamp',
field=models.DateTimeField(null=True),
),
migrations.AlterField(
model_name='ovhsmsgateway',
name='credit_threshold_alert',
field=models.PositiveIntegerField(default=500, verbose_name='Credit alert threshold'),
),
migrations.AlterField(
model_name='ovhsmsgateway',
name='password',
field=models.CharField(blank=True, help_text='Password for legacy API. This field is obsolete once keys and secret fields above are filled.', max_length=64, verbose_name='Password (deprecated)'),
),
migrations.AlterField(
model_name='ovhsmsgateway',
name='username',
field=models.CharField(help_text='API user created on the SMS account. This field is obsolete once keys and secret fields above are filled.', max_length=64, verbose_name='Username (deprecated)'),
),
]

View File

@ -2,9 +2,15 @@ import hashlib
import json
import requests
import time
from datetime import timedelta
from urllib.parse import urljoin
from django.contrib.postgres.fields import ArrayField
from django.conf import settings
from django.core.mail import send_mail
from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
@ -70,8 +76,15 @@ class OVHSMSGateway(SMSResource):
msg_class = models.IntegerField(choices=MESSAGES_CLASSES, default=1,
verbose_name=_('Message class'))
credit_threshold_alert = models.PositiveIntegerField(verbose_name=_('Credit alert threshold'),
default=100)
default=500)
credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0, editable=False)
alert_emails = ArrayField(
models.EmailField(blank=True),
blank=True,
null=True,
verbose_name=_('Email addresses to send credit alerts to'),
)
credit_alert_timestamp = models.DateTimeField(null=True)
TEST_DEFAULTS = {
'create_kwargs': {
@ -175,12 +188,6 @@ class OVHSMSGateway(SMSResource):
if self.credit_left < 0:
self.credit_left = 0
self.save(update_credit=False)
if self.credit_left < self.credit_threshold_alert:
ret['warning'] = 'credit level too low for %s: %s (threshold %s)' % (
self.slug,
self.credit_left,
self.credit_threshold_alert,
)
ret['credit_left'] = self.credit_left
ret['ovh_result'] = result
ret['sms_ids'] = result.get('ids', [])
@ -188,19 +195,42 @@ class OVHSMSGateway(SMSResource):
return ret
def update_credit_left(self):
if not self.uses_new_api:
return
result = self.request('get', endpoint='')
self.credit_left = result['creditsLeft']
self.save(update_credit=False)
def send_credit_alert_if_needed(self):
if self.credit_left >= self.credit_threshold_alert:
return
if self.credit_alert_timestamp and self.credit_alert_timestamp > timezone.now() - timedelta(days=1):
return # alerts are sent daily
ctx = {
'connector': self,
'connector_url': urljoin(settings.SITE_BASE_URL, self.get_absolute_url()),
}
subject = render_to_string('ovh/credit_alert_subject.txt', ctx).strip()
body = render_to_string('ovh/credit_alert_body.txt', ctx)
html_body = render_to_string('ovh/credit_alert_body.html', ctx)
send_mail(
subject,
body,
settings.DEFAULT_FROM_EMAIL,
self.alert_emails,
html_message=html_body,
)
self.credit_alert_timestamp = timezone.now()
self.save()
self.logger.warning('credit is too low, alerts were sent to %s', self.alert_emails)
def hourly(self):
super().hourly()
self.update_credit_left()
if self.uses_new_api:
self.update_credit_left()
self.send_credit_alert_if_needed()
def save(self, *args, update_credit=True, **kwargs):
super().save(*args, **kwargs)
if update_credit:
if update_credit and self.uses_new_api:
self.add_job('update_credit_left')
def send_msg_legacy(self, text, sender, destinations, **kwargs):

View File

@ -0,0 +1,22 @@
{% extends "emails/body_base.html" %}
{% load i18n %}
{% block content %}
<p>{% trans "Hi," %}</p>
<p>
{% blocktrans trimmed with name=connector.title credit_left=connector.credit_left %}
There are only {{ credit_left }} credits left for connector {{ name }}.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed with account=connector.account %}
Please add more credit as soon as possible for OVH account {{ account }}.
{% endblocktrans %}
</p>
{% with _("View connector page") as button_label %}
{% include "emails/button-link.html" with url=connector_url label=button_label %}
{% endwith %}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "emails/body_base.txt" %}
{% load i18n %}
{% block content %}{% autoescape off %}{% trans "Hi," %}
{% blocktrans trimmed with name=connector.title credit_left=connector.credit_left %}
There are only {{ credit_left }} credits left for connector {{ name }}.
{% endblocktrans %}
{% blocktrans trimmed with account=connector.account %}
Please add more credit as soon as possible for OVH account {{ account }}.
{% endblocktrans %}
{% trans "View connector page:" %} {{ connector_url }}
{% endautoescape %}
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends "emails/subject.txt" %}
{% load i18n %}
{% block email-subject %}{% autoescape off %}{% blocktrans trimmed with credit_left=connector.credit_left %}
OVH SMS alert: only {{ credit_left }} credits left
{% endblocktrans %}{% endautoescape %}{% endblock %}

View File

@ -210,6 +210,8 @@ REQUESTS_TIMEOUT = 25
# Passerelle can receive big requests (for example base64 encoded files)
DATA_UPLOAD_MAX_MEMORY_SIZE = 100*1024*1024
SITE_BASE_URL = 'http://localhost'
# List of passerelle.utils.Request response Content-Type to log
LOGGED_CONTENT_TYPES_MESSAGES = (
r'text/', r'application/(json|xml)'

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div style="max-width: 60ex;">
<div class="content">
{% block content %}
{{ content }}
{% endblock %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1 @@
{% block content %}{{ content }}{% endblock %}

View File

@ -0,0 +1 @@
<a href="{{ url }}">{{ label }}</a>

View File

@ -0,0 +1 @@
{% block email-subject %}{% endblock %}

View File

@ -289,3 +289,54 @@ def test_ovh_new_api_credit(app, freezer):
with utils.mock_url(ovh_url, resp, 200) as mocked:
connector.hourly()
assert connector.credit_left == 456
def test_ovh_alert_emails(app, freezer, mailoutbox):
connector = OVHSMSGateway.objects.create(
slug='test-ovh', title='Test OVH', account='sms-test42',
application_key='RHrTdU2oTsrVC0pu',
application_secret='CLjtS69tTcPgCKxedeoZlgMSoQGSiXMa',
consumer_key='iF0zi0MJrbjNcI3hvuvwkhNk8skrigxz',
credit_threshold_alert=100,
credit_left=102,
alert_emails=['test@entrouvert.org'],
)
api = ApiUser.objects.create(username='apiuser')
obj_type = ContentType.objects.get_for_model(connector)
AccessRight.objects.create(codename='can_send_messages', apiuser=api, resource_type=obj_type,
resource_pk=connector.pk)
freezer.move_to('2019-01-01 00:00:00')
resp = {'creditsLeft': 101}
ovh_url = connector.API_URL % {'serviceName': 'sms-test42'}
with utils.mock_url(ovh_url, resp, 200) as mocked:
connector.hourly()
assert len(mailoutbox) == 0
resp = {'creditsLeft': 99}
ovh_url = connector.API_URL % {'serviceName': 'sms-test42'}
with utils.mock_url(ovh_url, resp, 200) as mocked:
connector.hourly()
assert len(mailoutbox) == 1
mail = mailoutbox[0]
assert mail.recipients() == ['test@entrouvert.org']
assert mail.subject == 'OVH SMS alert: only 99 credits left'
for body in (mail.body, mail.alternatives[0][0]):
assert connector.account in body
assert connector.title in body
assert 'http://localhost/ovh/test-ovh/' in body
mailoutbox.clear()
# alert is sent again daily
freezer.move_to('2019-01-01 12:00:00')
resp = {'creditsLeft': 99}
ovh_url = connector.API_URL % {'serviceName': 'sms-test42'}
with utils.mock_url(ovh_url, resp, 200) as mocked:
connector.hourly()
assert len(mailoutbox) == 0
freezer.move_to('2019-01-02 01:00:07')
with utils.mock_url(ovh_url, resp, 200) as mocked:
connector.hourly()
assert len(mailoutbox) == 1