ovh: send email credit alerts (#42921)
This commit is contained in:
parent
153a91373d
commit
fc615aff90
|
@ -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)'),
|
||||
),
|
||||
]
|
|
@ -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):
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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)'
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
{% block content %}{{ content }}{% endblock %}
|
|
@ -0,0 +1 @@
|
|||
<a href="{{ url }}">{{ label }}</a>
|
|
@ -0,0 +1 @@
|
|||
{% block email-subject %}{% endblock %}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue