Projet

Général

Profil

0001-sfr_dmc-implement-SFR-DMC-sms-connector-20973.patch

Corentin Séchet, 02 mars 2022 14:22

Télécharger (12,4 ko)

Voir les différences:

Subject: [PATCH] sfr_dmc: implement SFR DMC sms connector (#20973)

 passerelle/apps/sfr_dmc/__init__.py           |   0
 .../apps/sfr_dmc/migrations/0001_initial.py   |  87 +++++++++++++
 .../apps/sfr_dmc/migrations/__init__.py       |   0
 passerelle/apps/sfr_dmc/models.py             | 122 ++++++++++++++++++
 passerelle/settings.py                        |   3 +-
 tests/test_sms.py                             |  36 ++++++
 6 files changed, 247 insertions(+), 1 deletion(-)
 create mode 100644 passerelle/apps/sfr_dmc/__init__.py
 create mode 100644 passerelle/apps/sfr_dmc/migrations/0001_initial.py
 create mode 100644 passerelle/apps/sfr_dmc/migrations/__init__.py
 create mode 100644 passerelle/apps/sfr_dmc/models.py
passerelle/apps/sfr_dmc/migrations/0001_initial.py
1
# Generated by Django 2.2.24 on 2022-02-09 11:10
2

  
3
import django.contrib.postgres.fields
4
from django.db import migrations, models
5

  
6
import passerelle.sms.models
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    initial = True
12

  
13
    dependencies = [
14
        ('base', '0029_auto_20210202_1627'),
15
    ]
16

  
17
    operations = [
18
        migrations.CreateModel(
19
            name='SfrDmcGateway',
20
            fields=[
21
                (
22
                    'id',
23
                    models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
24
                ),
25
                ('title', models.CharField(max_length=50, verbose_name='Title')),
26
                ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
27
                ('description', models.TextField(verbose_name='Description')),
28
                (
29
                    'default_country_code',
30
                    models.CharField(default='33', max_length=3, verbose_name='Default country code'),
31
                ),
32
                (
33
                    'default_trunk_prefix',
34
                    models.CharField(default='0', max_length=2, verbose_name='Default trunk prefix'),
35
                ),
36
                (
37
                    'max_message_length',
38
                    models.IntegerField(
39
                        default=2000,
40
                        help_text='Messages over this limit will be truncated.',
41
                        verbose_name='Maximum message length',
42
                    ),
43
                ),
44
                (
45
                    'authorized',
46
                    django.contrib.postgres.fields.ArrayField(
47
                        base_field=models.CharField(
48
                            choices=[
49
                                ('fr-metro', 'France mainland (+33 [67])'),
50
                                ('fr-domtom', 'France DOM/TOM (+262, etc.)'),
51
                                ('be', 'Belgian (+32 4[5-9]) '),
52
                                ('all', 'All'),
53
                            ],
54
                            max_length=32,
55
                            null=True,
56
                        ),
57
                        default=passerelle.sms.models.authorized_default,
58
                        size=None,
59
                        verbose_name='Authorized Countries',
60
                    ),
61
                ),
62
                (
63
                    'allow_premium_rate',
64
                    models.BooleanField(
65
                        default=False,
66
                        help_text='This option is only applyed to France mainland',
67
                        verbose_name='Allow premium rate numbers',
68
                    ),
69
                ),
70
                ('service_id', models.CharField(max_length=15, verbose_name='Service ID')),
71
                ('service_password', models.CharField(max_length=15, verbose_name='Service Password')),
72
                ('space_id', models.IntegerField(verbose_name='Space ID')),
73
                (
74
                    'users',
75
                    models.ManyToManyField(
76
                        blank=True,
77
                        related_name='_sfrdmcgateway_users_+',
78
                        related_query_name='+',
79
                        to='base.ApiUser',
80
                    ),
81
                ),
82
            ],
83
            options={
84
                'verbose_name': 'SFR DMC',
85
            },
86
        ),
87
    ]
passerelle/apps/sfr_dmc/models.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2022  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 json
17

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

  
21
from passerelle.sms.models import SMSResource
22
from passerelle.utils.jsonresponse import APIError
23

  
24

  
25
class SfrDmcGateway(SMSResource):
26
    service_id = models.CharField(verbose_name=_('Service ID'), max_length=15)
27
    service_password = models.CharField(verbose_name=_('Service Password'), max_length=15)
28
    space_id = models.IntegerField(verbose_name=_('Space ID'))
29

  
30
    TEST_DEFAULTS = {
31
        'create_kwargs': {
32
            'service_id': '1234',
33
            'service_password': 'krascuky',
34
            'space_id': '1234',
35
        },
36
        'test_vectors': [
37
            {
38
                'response': '',
39
                'result': {
40
                    'err': 1,
41
                    'err_desc': 'Bad JSON response',
42
                    'data': None,
43
                },
44
            },
45
            {
46
                'response': {'bad_json': True},
47
                'result': {
48
                    'err': 1,
49
                    'err_desc': 'Bad JSON response',
50
                    'data': None,
51
                },
52
            },
53
            {
54
                'response': {'success': False, 'no_error_detail_field': 'bad_json'},
55
                'result': {'err': 1, 'err_desc': 'Bad JSON response', 'data': None},
56
            },
57
            {
58
                'response': {'success': False, 'errorDetail': 'sfr error description'},
59
                'result': {
60
                    'err': 1,
61
                    'err_desc': 'sfr error description',
62
                    'data': None,
63
                },
64
            },
65
            {
66
                'response': {'success': True, 'response': ['1234', '5678']},
67
                'result': {
68
                    'err': 0,
69
                    'data': [
70
                        ['0033688888888', {'result': 'Envoi terminé', 'sms_id': 1234}],
71
                        ['0033677777777', {'result': 'Envoi terminé', 'sms_id': 1234}],
72
                    ],
73
                },
74
            },
75
        ],
76
    }
77
    URL = 'https://www.dmc.sfr-sh.fr/DmcWS/1.5.7'
78

  
79
    class Meta:
80
        verbose_name = _('SFR DMC')
81

  
82
    def send_msg(self, text, sender, destinations, **kwargs):
83
        """Send a SMS using the SFR DMC provider"""
84

  
85
        if len(destinations) > 20:
86
            raise APIError('you can\'t send more than 20 sms at once.')
87

  
88
        sfr_format_destinations = []
89
        for dest in destinations:
90
            sfr_format_destinations.append(f'+{dest[2:]}')  # SFR API only accepts '+' prefix
91

  
92
        response = self.requests.get(
93
            f'{self.URL}/JsonService/MessagesUnitairesWS/batchSingleCall',
94
            params={
95
                'authenticate': json.dumps(
96
                    {
97
                        'serviceId': self.service_id,
98
                        'servicePassword': self.service_password,
99
                        'spaceId': self.space_id,
100
                    }
101
                ),
102
                'messageUnitaires': json.dumps(
103
                    [
104
                        {'media': 'SMS', 'textMsg': text, 'from': sender, 'to': dest}
105
                        for dest in sfr_format_destinations
106
                    ]
107
                ),
108
            },
109
        )
110

  
111
        try:
112
            json_data = response.json()
113
        except ValueError:
114
            raise APIError('Bad JSON response')
115

  
116
        if 'success' not in json_data:
117
            raise APIError('Bad JSON response')
118
        elif not json_data['success']:
119
            if not 'errorDetail' in json_data:
120
                raise APIError('Bad JSON response')
121
            else:
122
                raise APIError(json_data['errorDetail'])
passerelle/settings.py
147 147
    'passerelle.apps.gdc',
148 148
    'passerelle.apps.gesbac',
149 149
    'passerelle.apps.jsondatastore',
150
    'passerelle.apps.sp_fr',
151 150
    'passerelle.apps.maelis',
152 151
    'passerelle.apps.mdel',
153 152
    'passerelle.apps.mdel_ddpacs',
......
162 161
    'passerelle.apps.photon',
163 162
    'passerelle.apps.plone_restapi',
164 163
    'passerelle.apps.sector',
164
    'passerelle.apps.sfr_dmc',
165 165
    'passerelle.apps.solis',
166
    'passerelle.apps.sp_fr',
166 167
    'passerelle.apps.twilio',
167 168
    'passerelle.apps.vivaticket',
168 169
    # backoffice templates and static
tests/test_sms.py
26 26
from django.utils.translation import ugettext as _
27 27
from requests import RequestException
28 28
from test_manager import login
29
from utils import FakedResponse
29 30

  
30 31
from passerelle.apps.choosit.models import ChoositSMSGateway
31 32
from passerelle.apps.ovh.models import OVHSMSGateway
33
from passerelle.apps.sfr_dmc.models import SfrDmcGateway
32 34
from passerelle.base.models import AccessRight, ApiUser, Job, ResourceLog
33 35
from passerelle.sms.models import SMSLog, SMSResource
34 36
from passerelle.utils.jsonresponse import APIError
......
707 709
    # invalid time_interval
708 710
    resp = app.get(url + '?time_interval=month')
709 711
    assert resp.json['err'] == 1
712

  
713

  
714
@pytest.mark.parametrize('connector', [SfrDmcGateway], indirect=True)
715
def test_sfr_max_message_error(connector):
716
    with pytest.raises(APIError, match='you can\'t send more than 20 sms at once.'):
717
        connector.send_msg('hello', '0033699999999', [f'00336888888{suffix:02d}' for suffix in range(0, 21)])
718

  
719

  
720
@pytest.mark.parametrize('connector', [SfrDmcGateway], indirect=True)
721
def test_sfr_prefix(connector):
722
    json_response = {
723
        'success': True,
724
        'response': {
725
            0: '4500283767',
726
            1: '4500283767',
727
        },
728
    }
729

  
730
    with mock.patch('passerelle.utils.Request.get') as mocked_get:
731
        mocked_get.return_value = FakedResponse(content=json.dumps(json_response), status_code=200)
732
        connector.send_msg('hello', '0033699999999', ['0033688888888'])
733
        mocked_get.assert_called_once_with(
734
            'https://www.dmc.sfr-sh.fr/DmcWS/1.5.7/JsonService/MessagesUnitairesWS/batchSingleCall',
735
            params={
736
                'authenticate': json.dumps(
737
                    {'serviceId': '1234', 'servicePassword': 'krascuky', 'spaceId': '1234'}
738
                ),
739
                'messageUnitaires': json.dumps(
740
                    [
741
                        {'media': 'SMS', 'textMsg': 'hello', 'from': '0033699999999', 'to': '+33688888888'},
742
                    ]
743
                ),
744
            },
745
        )
710
-