Projet

Général

Profil

0001-ovh-update-credit-left-42921.patch

Valentin Deniaud, 08 octobre 2020 11:44

Télécharger (10,7 ko)

Voir les différences:

Subject: [PATCH] ovh: update credit left (#42921)

 .../ovh/migrations/0010_auto_20201008_1126.py | 20 +++++++
 passerelle/apps/ovh/models.py                 | 60 +++++++++++++------
 .../templates/ovh/ovhsmsgateway_detail.html   | 15 +++++
 tests/test_sms.py                             | 46 ++++++++++++--
 4 files changed, 118 insertions(+), 23 deletions(-)
 create mode 100644 passerelle/apps/ovh/migrations/0010_auto_20201008_1126.py
 create mode 100644 passerelle/apps/ovh/templates/ovh/ovhsmsgateway_detail.html
passerelle/apps/ovh/migrations/0010_auto_20201008_1126.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-10-08 09:26
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('ovh', '0009_auto_20200730_1047'),
12
    ]
13

  
14
    operations = [
15
        migrations.AlterField(
16
            model_name='ovhsmsgateway',
17
            name='credit_left',
18
            field=models.PositiveIntegerField(default=0, editable=False, verbose_name='Credit left'),
19
        ),
20
    ]
passerelle/apps/ovh/models.py
2 2
import json
3 3
import requests
4 4
import time
5
from urllib.parse import urljoin
5 6

  
6 7
from django.db import models
7 8
from django.utils.encoding import force_text
......
13 14

  
14 15
class OVHSMSGateway(SMSResource):
15 16
    documentation_url = 'https://doc-publik.entrouvert.com/admin-fonctionnel/les-tutos/configuration-envoi-sms/'
16
    hide_description_fields = ['account']
17
    API_URL = 'https://eu.api.ovh.com/1.0/sms/%(serviceName)s/users/%(login)s/jobs/'
17
    hide_description_fields = ['account', 'credit_left']
18
    API_URL = 'https://eu.api.ovh.com/1.0/sms/%(serviceName)s/'
18 19
    URL = 'https://www.ovh.com/cgi-bin/sms/http2sms.cgi'
19 20
    MESSAGES_CLASSES = (
20 21
        (0, _('Message are directly shown to users on phone screen '
......
69 70
                                    verbose_name=_('Message class'))
70 71
    credit_threshold_alert = models.PositiveIntegerField(verbose_name=_('Credit alert threshold'),
71 72
                                                         default=100)
72
    credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0)
73
    credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0, editable=False)
73 74

  
74 75
    TEST_DEFAULTS = {
75 76
        'create_kwargs': {
......
114 115
        verbose_name = 'OVH'
115 116
        db_table = 'sms_ovh'
116 117

  
117
    def send_msg(self, text, sender, destinations, **kwargs):
118
        if not (self.application_key and self.consumer_key and self.application_secret):
119
            return self.send_msg_legacy(text, sender, destinations, **kwargs)
118
    @property
119
    def uses_new_api(self):
120
        return self.application_key and self.consumer_key and self.application_secret
120 121

  
122
    def request(self, method, endpoint, **kwargs):
121 123
        url = self.API_URL % {'serviceName': self.account, 'login': self.username}
122
        body = {
123
            'sender': sender,
124
            'receivers': destinations,
125
            'message': text,
126
            'class': self.NEW_MESSAGES_CLASSES[self.msg_class],
127
        }
128
        if not kwargs['stop']:
129
            body.update({'noStopClause': 1})
124
        url = urljoin(url, endpoint)
130 125

  
131 126
        # sign request
127
        body = json.dumps(kwargs['json']) if 'json' in kwargs else ''
132 128
        now = str(int(time.time()))
133 129
        signature = hashlib.sha1()
134
        to_sign = "+".join((self.application_secret, self.consumer_key, 'POST', url, json.dumps(body), now))
130
        to_sign = "+".join((self.application_secret, self.consumer_key, method.upper(), url, body, now))
135 131
        signature.update(to_sign.encode())
136 132

  
137 133
        headers = {
......
142 138
        }
143 139

  
144 140
        try:
145
            response = self.requests.post(url, headers=headers, json=body)
141
            response = self.requests.request(method, url, headers=headers, **kwargs)
146 142
        except requests.RequestException as e:
147 143
            raise APIError('OVH error: POST failed, %s' % e)
148 144
        else:
......
154 150
            response.raise_for_status()
155 151
        except requests.RequestException as e:
156 152
            raise APIError('OVH error: %s "%s"' % (e, result))
153
        return result
154

  
155
    def send_msg(self, text, sender, destinations, **kwargs):
156
        if not self.uses_new_api:
157
            return self.send_msg_legacy(text, sender, destinations, **kwargs)
158

  
159
        body = {
160
            'sender': sender,
161
            'receivers': destinations,
162
            'message': text,
163
            'class': self.NEW_MESSAGES_CLASSES[self.msg_class],
164
        }
165
        if not kwargs['stop']:
166
            body.update({'noStopClause': 1})
167

  
168
        result = self.request('post', 'jobs/', json=body)
157 169

  
158 170
        ret = {}
159 171
        credits_removed = result['totalCreditsRemoved']
......
161 173
        self.credit_left -= credits_removed
162 174
        if self.credit_left < 0:
163 175
            self.credit_left = 0
164
        self.save()
176
        self.save(update_credit=False)
165 177
        if self.credit_left < self.credit_threshold_alert:
166 178
            ret['warning'] = 'credit level too low for %s: %s (threshold %s)' % (
167 179
                self.slug,
......
174 186

  
175 187
        return ret
176 188

  
189
    def update_credit_left(self):
190
        result = self.request('get', endpoint='')
191
        self.credit_left = result['creditsLeft']
192
        self.save(update_credit=False)
193

  
194
    def hourly(self):
195
        super().hourly()
196
        self.update_credit_left()
197

  
198
    def save(self, *args, update_credit=True, **kwargs):
199
        super().save(*args, **kwargs)
200
        if update_credit:
201
            self.add_job('update_credit_left')
202

  
177 203
    def send_msg_legacy(self, text, sender, destinations, **kwargs):
178 204
        """Send a SMS using the HTTP2 endpoint"""
179 205
        if not self.password:
passerelle/apps/ovh/templates/ovh/ovhsmsgateway_detail.html
1
{% extends "passerelle/manage/messages_service_view.html" %}
2
{% load i18n passerelle %}
3

  
4
{% block description %}
5
{{ block.super }}
6
{% if object.uses_new_api %}
7
<p>
8
{% if object.credit_left %}
9
<b>{% trans "Credit left:" %}</b>  {{ object.credit_left }}
10
{% else %}
11
<b>{% trans "There is no credit left." %}</b>
12
{% endif %}
13
</p>
14
{% endif %}
15
{% endblock %}
tests/test_sms.py
68 68
    test_vectors = getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', [])
69 69
    total = len(test_vectors)
70 70
    nb_failed = 0
71
    assert Job.objects.count() == 0
71
    assert Job.objects.filter(method_name='send_job').count() == 0
72 72
    for test_vector in test_vectors:
73 73

  
74 74
        # register job
75 75
        freezer.move_to('2019-01-01 00:00:00')
76 76
        result = app.post_json(path, params=payload)
77 77
        assert result.json['err'] == 0
78
        job_id = Job.objects.get(status='registered').id
78
        job_id = Job.objects.get(method_name='send_job', status='registered').id
79 79

  
80 80
        # perform job
81 81
        freezer.move_to('2019-01-01 01:00:03')
......
91 91
            nb_failed += 1
92 92
        else:
93 93
            assert job.status == 'completed'
94
    assert Job.objects.count() == total
94
    assert Job.objects.filter(method_name='send_job').count() == total
95 95
    assert SMSLog.objects.count() == total - nb_failed
96 96

  
97 97

  
......
185 185

  
186 186
def test_ovh_new_api(app, freezer):
187 187
    connector = OVHSMSGateway.objects.create(
188
        slug='ovh', account='sms-test42', username='john',
188
        slug='ovh', account='sms-test42',
189 189
        application_key='RHrTdU2oTsrVC0pu',
190 190
        application_secret='CLjtS69tTcPgCKxedeoZlgMSoQGSiXMa',
191 191
        consumer_key='iF0zi0MJrbjNcI3hvuvwkhNk8skrigxz'
......
207 207
    path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug)
208 208
    result = app.post_json(path, params=payload)
209 209
    assert result.json['err'] == 0
210
    job_id = Job.objects.get(status='registered').id
210
    job_id = Job.objects.get(method_name='send_job', status='registered').id
211 211

  
212 212
    # perform job
213 213
    freezer.move_to('2019-01-01 01:00:03')
......
217 217
        'ids': [241615100],
218 218
        'invalidReceivers': []
219 219
    }
220
    url = connector.API_URL % {'serviceName': 'sms-test42', 'login': 'john'}
220
    base_url = connector.API_URL % {'serviceName': 'sms-test42'}
221
    url = base_url + 'jobs/'
221 222
    with utils.mock_url(url, resp, 200) as mocked:
222 223
        connector.jobs()
223 224
    job = Job.objects.get(id=job_id)
......
225 226

  
226 227
    request = mocked.handlers[0].call['requests'][0]
227 228
    assert 'X-Ovh-Signature' in request.headers
229

  
230

  
231
def test_ovh_new_api_credit(app, freezer):
232
    connector = OVHSMSGateway.objects.create(
233
        slug='ovh', account='sms-test42',
234
        application_key='RHrTdU2oTsrVC0pu',
235
        application_secret='CLjtS69tTcPgCKxedeoZlgMSoQGSiXMa',
236
        consumer_key='iF0zi0MJrbjNcI3hvuvwkhNk8skrigxz'
237
    )
238

  
239
    manager_url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug)
240
    resp = app.get(manager_url)
241
    assert 'no credit left' in resp.text
242

  
243
    # a job to update credit was added on connector creation
244
    resp = {
245
        'creditsLeft': 123,
246
    }
247
    ovh_url = connector.API_URL % {'serviceName': 'sms-test42'}
248
    with utils.mock_url(ovh_url, resp, 200) as mocked:
249
        connector.jobs()
250
    assert connector.credit_left == 123
251

  
252
    resp = app.get(manager_url)
253
    assert '123' in resp.text
254

  
255
    # hourly update
256
    resp = {
257
        'creditsLeft': 456,
258
    }
259
    with utils.mock_url(ovh_url, resp, 200) as mocked:
260
        connector.hourly()
261
    assert connector.credit_left == 456
228
-