0001-ovh-retry-on-too-many-requests-error-44730.patch
passerelle/apps/ovh/models.py | ||
---|---|---|
1 | 1 |
import hashlib |
2 | 2 |
import json |
3 |
import random |
|
3 | 4 |
import time |
4 | 5 |
from datetime import timedelta |
5 | 6 |
from urllib.parse import urljoin |
6 | 7 | |
7 | 8 |
import requests |
8 | 9 |
from django.conf import settings |
9 | 10 |
from django.contrib.postgres.fields import ArrayField |
10 | 11 |
from django.core.mail import send_mail |
11 | 12 |
from django.db import models |
12 | 13 |
from django.template.loader import render_to_string |
13 | 14 |
from django.utils import timezone |
14 | 15 |
from django.utils.encoding import force_text |
15 | 16 |
from django.utils.translation import ugettext_lazy as _ |
16 | 17 | |
18 |
from passerelle.base.models import SkipJob |
|
17 | 19 |
from passerelle.sms.models import SMSResource |
18 | 20 |
from passerelle.utils.jsonresponse import APIError |
19 | 21 | |
20 | 22 | |
21 | 23 |
class OVHSMSGateway(SMSResource): |
22 | 24 |
documentation_url = ( |
23 | 25 |
'https://doc-publik.entrouvert.com/admin-fonctionnel/les-tutos/configuration-envoi-sms/' |
24 | 26 |
) |
... | ... | |
275 | 277 |
self.slug, |
276 | 278 |
credit_left, |
277 | 279 |
self.credit_threshold_alert, |
278 | 280 |
) |
279 | 281 |
ret['credit_left'] = credit_left |
280 | 282 |
ret['ovh_result'] = result |
281 | 283 |
ret['sms_ids'] = result.get('SmsIds', []) |
282 | 284 |
return ret |
285 |
elif result['status'] == 429 and 'Please retry' in result.get('message'): |
|
286 |
raise SkipJob(after_timestamp=random.randint(4, 10)) |
|
283 | 287 |
else: |
284 | 288 |
raise APIError('OVH error: %r' % result) |
passerelle/sms/views.py | ||
---|---|---|
4 | 4 |
from django.contrib import messages |
5 | 5 |
from django.db.models import Count |
6 | 6 |
from django.db.models.functions import TruncDay |
7 | 7 |
from django.http import JsonResponse |
8 | 8 |
from django.utils.timezone import make_aware |
9 | 9 |
from django.utils.translation import ugettext_lazy as _ |
10 | 10 |
from django.views.generic import FormView, View |
11 | 11 | |
12 |
from passerelle.base.models import SkipJob |
|
12 | 13 |
from passerelle.utils.jsonresponse import APIError |
13 | 14 |
from passerelle.views import GenericConnectorMixin |
14 | 15 | |
15 | 16 |
from .forms import SmsTestSendForm |
16 | 17 |
from .models import SMSLog |
17 | 18 | |
18 | 19 | |
19 | 20 |
class SmsTestSendView(GenericConnectorMixin, FormView): |
... | ... | |
33 | 34 |
number = form.cleaned_data['number'] |
34 | 35 |
sender = form.cleaned_data['sender'] |
35 | 36 |
message = form.cleaned_data['message'] |
36 | 37 |
connector = self.get_object() |
37 | 38 |
try: |
38 | 39 |
number = connector.clean_numbers([number])[0] |
39 | 40 |
number = connector.authorize_numbers([number])[0][0] |
40 | 41 |
connector.send_msg(text=message, sender=sender, destinations=[number], stop=False) |
41 |
except APIError as exc:
|
|
42 |
except (APIError, SkipJob) as exc:
|
|
42 | 43 |
messages.error(self.request, _('Sending SMS fails: %s' % exc)) |
43 | 44 |
else: |
44 | 45 |
messages.success(self.request, _('An SMS was just sent')) |
45 | 46 |
return super(SmsTestSendView, self).form_valid(form) |
46 | 47 | |
47 | 48 | |
48 | 49 |
class SmsStatisticsView(View): |
49 | 50 |
def get(self, request, *args, **kwargs): |
tests/test_sms.py | ||
---|---|---|
173 | 173 |
assert test_vector['result']['err_desc'] in job.status_details['error_summary'] |
174 | 174 |
nb_failed += 1 |
175 | 175 |
else: |
176 | 176 |
assert job.status == 'completed' |
177 | 177 |
assert Job.objects.filter(method_name='send_job').count() == total |
178 | 178 |
assert SMSLog.objects.count() == total - nb_failed |
179 | 179 | |
180 | 180 | |
181 |
@pytest.mark.parametrize('connector', [OVHSMSGateway], indirect=True) |
|
182 |
def test_sms_legacy_retry_after_error(app, connector, freezer): |
|
183 |
path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug) |
|
184 |
assert not SMSLog.objects.filter(appname=connector.get_connector_slug(), slug=connector.slug).exists() |
|
185 | ||
186 |
payload = { |
|
187 |
'message': 'plop', |
|
188 |
'from': '+33699999999', |
|
189 |
'to': ['+33688888888'], |
|
190 |
} |
|
191 |
freezer.move_to('2019-01-01 00:00:00') |
|
192 |
result = app.post_json(path, params=payload) |
|
193 |
assert result.json['err'] == 0 |
|
194 |
assert Job.objects.exists() |
|
195 |
assert Job.objects.get().status == 'registered' |
|
196 |
assert not Job.objects.get().after_timestamp |
|
197 |
with utils.mock_url( |
|
198 |
url=connector.URL, |
|
199 |
response={'status': 429, 'message': 'Too much requests.\nPlease retry in 3 seconds.'}, |
|
200 |
): |
|
201 |
connector.jobs() |
|
202 |
assert Job.objects.get().status == 'registered' |
|
203 |
assert Job.objects.get().after_timestamp |
|
204 | ||
205 |
with utils.mock_url(url=connector.URL, response={'status': 100, 'credit_left': 22}): |
|
206 |
connector.jobs() |
|
207 |
assert Job.objects.get().status == 'registered' |
|
208 | ||
209 |
freezer.move_to('2019-01-01 00:00:11') |
|
210 |
with utils.mock_url(url=connector.URL, response={'status': 100, 'creditLeft': 22}): |
|
211 |
connector.jobs() |
|
212 |
assert Job.objects.get().status == 'completed' |
|
213 |
assert SMSLog.objects.exists() |
|
214 | ||
215 | ||
181 | 216 |
def test_manage_views(admin_user, app, connector): |
182 | 217 |
url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug) |
183 | 218 |
resp = app.get(url) |
184 | 219 |
assert 'Endpoints' in resp.text |
185 | 220 |
assert not 'accessright/add' in resp.text |
186 | 221 |
app = login(app) |
187 | 222 |
resp = app.get(url) |
188 | 223 |
description_fields = [ |
189 |
- |