0001-sms-add-endpoint-to-send-SMS-asynchronously-21465.patch
passerelle/base/models.py | ||
---|---|---|
932 | 932 |
elif number.startswith(default_trunk_prefix): |
933 | 933 |
number = '00' + default_country_code + number[len(default_trunk_prefix):] |
934 | 934 |
else: |
935 | 935 |
raise APIError('phone number %r is unsupported (no international prefix, ' |
936 | 936 |
'no local trunk prefix)' % number) |
937 | 937 |
numbers.append(number) |
938 | 938 |
return numbers |
939 | 939 | |
940 |
@endpoint(perm='can_send_messages', methods=['post']) |
|
941 |
def send(self, request, *args, **kwargs): |
|
940 |
def get_parameters(self, request, *args, **kwargs): |
|
942 | 941 |
try: |
943 | 942 |
data = json_loads(request.body) |
944 | 943 |
assert isinstance(data, dict), 'JSON payload is not a dict' |
945 | 944 |
assert 'message' in data, 'missing "message" in JSON payload' |
946 | 945 |
assert 'from' in data, 'missing "from" in JSON payload' |
947 | 946 |
assert 'to' in data, 'missing "to" in JSON payload' |
948 | 947 |
assert isinstance(data['message'], six.text_type), 'message is not a string' |
949 | 948 |
assert isinstance(data['from'], six.text_type), 'from is not a string' |
950 | 949 |
assert all(map(lambda x: isinstance(x, six.text_type), data['to'])), \ |
951 | 950 |
'to is not a list of strings' |
952 | 951 |
except (ValueError, AssertionError) as e: |
953 | 952 |
raise APIError('Payload error: %s' % e) |
954 |
data['message'] = data['message'][:self.max_message_length] |
|
955 |
logging.info('sending message %r to %r with sending number %r', |
|
956 |
data['message'], data['to'], data['from']) |
|
957 |
stop = not bool('nostop' in request.GET) |
|
958 |
result = {'data': self.send_msg(data['message'], data['from'], data['to'], stop=stop)} |
|
953 |
parameters = { |
|
954 |
'text': data['message'][:self.max_message_length], |
|
955 |
'sender': data['from'], |
|
956 |
'destinations': data['to'], |
|
957 |
'stop': not bool('nostop' in request.GET) |
|
958 |
} |
|
959 |
logging.info( |
|
960 |
'sending message %r to %r with sending number %r', |
|
961 |
parameters['text'], parameters['destinations'], parameters['sender']) |
|
962 |
return parameters |
|
963 | ||
964 |
def call_send_msg(self, *args, **kwargs): |
|
965 |
resp = self.send_msg(**kwargs) |
|
959 | 966 |
SMSLog.objects.create(appname=self.get_connector_slug(), slug=self.slug) |
967 |
return resp |
|
968 | ||
969 |
@endpoint(perm='can_send_messages', methods=['post']) |
|
970 |
def send(self, request, *args, **kwargs): |
|
971 |
parameters = self.get_parameters(request, *args, **kwargs) |
|
972 |
result = {'data': self.call_send_msg(**parameters)} |
|
960 | 973 |
return result |
961 | 974 | |
975 |
@endpoint(perm='can_send_messages', methods=['post']) |
|
976 |
def send_async(self, request, *args, **kwargs): |
|
977 |
parameters = self.get_parameters(request, *args, **kwargs) |
|
978 |
self.add_job('call_send_msg', **parameters) |
|
979 |
return {'err': 0, 'data': parameters} |
|
980 | ||
962 | 981 |
class Meta: |
963 | 982 |
abstract = True |
964 | 983 | |
965 | 984 | |
966 | 985 |
@six.python_2_unicode_compatible |
967 | 986 |
class SMSLog(models.Model): |
968 | 987 |
timestamp = models.DateTimeField(auto_now_add=True) |
969 | 988 |
appname = models.CharField(max_length=128, verbose_name='appname', null=True) |
tests/test_sms.py | ||
---|---|---|
1 | 1 |
import mock |
2 | 2 |
import pytest |
3 | 3 | |
4 | 4 |
from django.contrib.contenttypes.models import ContentType |
5 | 5 | |
6 | 6 |
from passerelle.apps.ovh.models import OVHSMSGateway |
7 |
from passerelle.base.models import ApiUser, AccessRight, SMSResource, SMSLog |
|
7 |
from passerelle.base.models import ApiUser, AccessRight, Job, SMSResource, SMSLog
|
|
8 | 8 | |
9 | 9 |
from test_manager import login, admin_user |
10 | 10 | |
11 | 11 |
import utils |
12 | 12 | |
13 | 13 |
pytestmark = pytest.mark.django_db |
14 | 14 | |
15 | 15 |
klasses = SMSResource.__subclasses__() |
... | ... | |
61 | 61 |
test_vector.get('response', ''), |
62 | 62 |
test_vector.get('status_code', 200)): |
63 | 63 | |
64 | 64 |
result = app.post_json(path, params=payload) |
65 | 65 |
for key, value in test_vector['result'].items(): |
66 | 66 |
assert key in result.json |
67 | 67 |
assert result.json[key] == value |
68 | 68 | |
69 |
def test_send_async(app, connector): |
|
70 |
path = '/%s/%s/send_async/' % (connector.get_connector_slug(), connector.slug) |
|
71 |
result = app.post_json(path, params={}) |
|
72 |
assert result.json['err'] == 1 |
|
73 |
assert result.json['err_desc'].startswith('Payload error: ') |
|
74 | ||
75 |
payload = { |
|
76 |
'message': 'hello', |
|
77 |
'from': '+33699999999', |
|
78 |
'to': ['+33688888888', '+33677777777'], |
|
79 |
} |
|
80 |
test_vectors = getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', []) |
|
81 |
total = len(test_vectors) |
|
82 |
nb_failed = 0 |
|
83 |
assert Job.objects.count() == 0 |
|
84 |
for test_vector in test_vectors: |
|
85 | ||
86 |
# register job |
|
87 |
result = app.post_json(path, params=payload) |
|
88 |
job = Job.objects.get(status='registered') |
|
89 | ||
90 |
# perform job |
|
91 |
with utils.mock_url( |
|
92 |
connector.URL, |
|
93 |
test_vector.get('response', ''), |
|
94 |
test_vector.get('status_code', 200)): |
|
95 |
connector.jobs() |
|
96 |
job = Job.objects.get(id=job.id) |
|
97 |
if job.status == 'failed': |
|
98 |
assert test_vector['result']['err_desc'] in job.status_details['error_summary'] |
|
99 |
nb_failed += 1 |
|
100 |
else: |
|
101 |
assert job.status == 'completed' |
|
102 | ||
103 |
assert Job.objects.count() == total |
|
104 |
assert SMSLog.objects.count() == total - nb_failed |
|
69 | 105 | |
70 | 106 |
def test_manage_views(admin_user, app, connector): |
71 | 107 |
url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug) |
72 | 108 |
resp = app.get(url) |
73 | 109 |
assert 'Endpoints' in resp.text |
74 | 110 |
assert not 'accessright/add' in resp.text |
75 | 111 |
app = login(app) |
76 | 112 |
resp = app.get(url) |
... | ... | |
86 | 122 |
payload = { |
87 | 123 |
'message': message_above_limit, |
88 | 124 |
'from': '+33699999999', |
89 | 125 |
'to': ['+33688888888'], |
90 | 126 |
} |
91 | 127 |
with mock.patch.object(OVHSMSGateway, 'send_msg') as send_function: |
92 | 128 |
send_function.return_value = {} |
93 | 129 |
result = app.post_json(path, params=payload) |
94 |
assert send_function.call_args[0][0] == 'a' * connector.max_message_length
|
|
130 |
assert send_function.call_args[1]['text'] == 'a' * connector.max_message_length
|
|
95 | 131 | |
96 | 132 | |
97 | 133 |
@pytest.mark.parametrize('connector', [OVHSMSGateway], indirect=True) |
98 | 134 |
def test_sms_log(app, connector): |
99 | 135 |
path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug) |
100 | 136 |
assert not SMSLog.objects.filter(appname=connector.get_connector_slug(), slug=connector.slug).exists() |
101 | 137 | |
102 | 138 |
payload = { |
103 |
- |