Projet

Général

Profil

0001-sms-add-a-twilio-connector-19663.patch

Nicolas Roche, 10 avril 2020 11:39

Télécharger (7,87 ko)

Voir les différences:

Subject: [PATCH] sms: add a twilio connector (#19663)

 passerelle/apps/twilio/__init__.py            |  0
 .../apps/twilio/migrations/0001_initial.py    | 34 +++++++
 passerelle/apps/twilio/migrations/__init__.py |  0
 passerelle/apps/twilio/models.py              | 90 +++++++++++++++++++
 passerelle/settings.py                        |  1 +
 tests/test_sms.py                             |  6 +-
 6 files changed, 130 insertions(+), 1 deletion(-)
 create mode 100644 passerelle/apps/twilio/__init__.py
 create mode 100644 passerelle/apps/twilio/migrations/0001_initial.py
 create mode 100644 passerelle/apps/twilio/migrations/__init__.py
 create mode 100644 passerelle/apps/twilio/models.py
passerelle/apps/twilio/migrations/0001_initial.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-04-09 18:07
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    initial = True
11

  
12
    dependencies = [
13
        ('base', '0018_smslog'),
14
    ]
15

  
16
    operations = [
17
        migrations.CreateModel(
18
            name='TwilioSMSGateway',
19
            fields=[
20
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
                ('title', models.CharField(max_length=50, verbose_name='Title')),
22
                ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
23
                ('description', models.TextField(verbose_name='Description')),
24
                ('max_message_length', models.IntegerField(default=160, verbose_name='Maximum message length')),
25
                ('account_sid', models.CharField(max_length=64, verbose_name='Account Sid')),
26
                ('auth_token', models.CharField(max_length=64, verbose_name='Auth Token')),
27
                ('users', models.ManyToManyField(blank=True, related_name='_twiliosmsgateway_users_+', related_query_name='+', to='base.ApiUser')),
28
            ],
29
            options={
30
                'verbose_name': 'Twilio',
31
                'db_table': 'sms_twilio',
32
            },
33
        ),
34
    ]
passerelle/apps/twilio/models.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2020  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
import requests
17
from requests.auth import HTTPBasicAuth
18

  
19
from django.db import models
20
from django.utils.translation import ugettext_lazy as _
21

  
22
from passerelle.utils.jsonresponse import APIError
23
from passerelle.base.models import SMSResource
24

  
25

  
26
class TwilioSMSGateway(SMSResource):
27
    account_sid = models.CharField(verbose_name=_('Account Sid'), max_length=64)
28
    auth_token = models.CharField(verbose_name=_('Auth Token'), max_length=64)
29

  
30
    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
31

  
32
    class Meta:
33
        verbose_name = 'Twilio'
34
        db_table = 'sms_twilio'
35

  
36
    TEST_DEFAULTS = {
37
        'create_kwargs': {
38
            'account_sid': 'ACxxx',
39
            'auth_token': 'yyy',
40
        },
41
        'test_vectors': [
42
            {
43
                'status_code': 400,
44
                'response': 'my error message',
45
                'result': {
46
                    'err': 1,
47
                    'err_desc': 'Twilio error: some destinations failed',
48
                    'data': [
49
                        ['+33688888888', "Twilio error: my error message"],
50
                        ['+33677777777', "Twilio error: my error message"],
51
                    ],
52
                }
53
            },
54
            {
55
                'status_code': 201,
56
                'result': {
57
                    'err': 0,
58
                    'data': None,
59
                }
60
            }
61
        ],
62
    }
63
    URL = 'https://api.twilio.com/2010-04-01/Accounts'
64

  
65
    def send_msg(self, text, sender, destinations, **kwargs):
66
        """Send a SMS using the Twilio provider"""
67

  
68
        url = '%s/%s/Messages.json' % (TwilioSMSGateway.URL, self.account_sid)
69
        auth = HTTPBasicAuth(self.account_sid, self.auth_token)
70
        results = []
71
        for dest in destinations:
72
            params = {
73
                'Body': text,
74
                'From': sender,
75
                'To': dest
76
            }
77
            try:
78
                resp = self.requests.post(url, params, auth=auth)
79
            except requests.RequestException as e:
80
                results.append('Twilio error: POST failed, %s' % e)
81
            else:
82
                if resp.status_code != 201:
83
                    results.append('Twilio error: %s' % resp.text)
84
                else:
85
                    results.append(0)
86
        if any(results):
87
            raise APIError(
88
                'Twilio error: some destinations failed',
89
                data=list(zip(destinations, results)))
90
        return None
passerelle/settings.py
149 149
    'passerelle.apps.opengis',
150 150
    'passerelle.apps.orange',
151 151
    'passerelle.apps.ovh',
152 152
    'passerelle.apps.oxyd',
153 153
    'passerelle.apps.pastell',
154 154
    'passerelle.apps.phonecalls',
155 155
    'passerelle.apps.solis',
156 156
    'passerelle.apps.vivaticket',
157
    'passerelle.apps.twilio',
157 158
    # backoffice templates and static
158 159
    'gadjo',
159 160
)
160 161

  
161 162
# disable some applications for now
162 163
PASSERELLE_APP_BDP_ENABLED = False
163 164
PASSERELLE_APP_GDC_ENABLED = False
164 165
PASSERELLE_APP_PASTELL_ENABLED = False
tests/test_sms.py
51 51
    assert result.json['err_desc'].startswith('Payload error: ')
52 52

  
53 53
    payload = {
54 54
        'message': 'hello',
55 55
        'from': '+33699999999',
56 56
        'to': ['+33688888888', '+33677777777'],
57 57
    }
58 58
    for test_vector in getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', []):
59
        with utils.mock_url(connector.URL, test_vector['response']):
59
        with utils.mock_url(
60
                connector.URL,
61
                test_vector.get('response', ''),
62
                test_vector.get('status_code', 200)):
63

  
60 64
            result = app.post_json(path, params=payload)
61 65
            for key, value in test_vector['result'].items():
62 66
                assert key in result.json
63 67
                assert result.json[key] == value
64 68

  
65 69

  
66 70
def test_manage_views(admin_user, app, connector):
67 71
    url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug)
68
-