Projet

Général

Profil

0001-sms-turn-mixin-into-abstract-class-39654.patch

Valentin Deniaud, 10 mars 2020 16:57

Télécharger (15,2 ko)

Voir les différences:

Subject: [PATCH 1/2] sms: turn mixin into abstract class (#39654)

 .../apps/choosit/migrations/0001_initial.py   |  2 +-
 passerelle/apps/choosit/models.py             |  5 +-
 .../apps/mobyt/migrations/0001_initial.py     |  2 +-
 passerelle/apps/mobyt/models.py               |  5 +-
 .../apps/orange/migrations/0001_initial.py    |  2 +-
 passerelle/apps/orange/models.py              |  5 +-
 .../apps/ovh/migrations/0001_initial.py       |  2 +-
 passerelle/apps/ovh/models.py                 |  5 +-
 .../apps/oxyd/migrations/0001_initial.py      |  2 +-
 passerelle/apps/oxyd/models.py                |  5 +-
 passerelle/base/models.py                     | 54 +++++++++++++++++
 passerelle/sms/__init__.py                    | 58 -------------------
 tests/test_sms.py                             | 17 +++---
 13 files changed, 77 insertions(+), 87 deletions(-)
 delete mode 100644 passerelle/sms/__init__.py
passerelle/apps/choosit/migrations/0001_initial.py
56 56
                'db_table': 'sms_choosit',
57 57
                'verbose_name': 'Choosit',
58 58
            },
59
            bases=(models.Model, passerelle.sms.SMSGatewayMixin),
59
            bases=(models.Model,),
60 60
        ),
61 61
        migrations.AddField(
62 62
            model_name='choositregistergateway',
passerelle/apps/choosit/models.py
7 7
from django.db import models
8 8

  
9 9
from passerelle.utils.jsonresponse import APIError
10
from passerelle.base.models import BaseResource
11
from passerelle.sms import SMSGatewayMixin
10
from passerelle.base.models import BaseResource, SMSResource
12 11

  
13 12

  
14
class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
13
class ChoositSMSGateway(BaseResource, SMSResource):
15 14
    key = models.CharField(verbose_name=_('Key'), max_length=64)
16 15
    default_country_code = models.CharField(verbose_name=_('Default country code'), max_length=3,
17 16
                                            default=u'33')
passerelle/apps/mobyt/migrations/0001_initial.py
29 29
                'db_table': 'sms_mobyt',
30 30
                'verbose_name': 'Mobyt',
31 31
            },
32
            bases=(models.Model, passerelle.sms.SMSGatewayMixin),
32
            bases=(models.Model,),
33 33
        ),
34 34
    ]
passerelle/apps/mobyt/models.py
4 4
import requests
5 5

  
6 6
from passerelle.utils.jsonresponse import APIError
7
from passerelle.base.models import BaseResource
8
from passerelle.sms import SMSGatewayMixin
7
from passerelle.base.models import BaseResource, SMSResource
9 8

  
10 9

  
11
class MobytSMSGateway(BaseResource, SMSGatewayMixin):
10
class MobytSMSGateway(BaseResource, SMSResource):
12 11
    URL = 'http://multilevel.mobyt.fr/sms/batch.php'
13 12
    MESSAGES_QUALITIES = (
14 13
        ('l', _('sms direct')),
passerelle/apps/orange/migrations/0001_initial.py
26 26
                'db_table': 'sms_orange',
27 27
                'verbose_name': 'Orange',
28 28
            },
29
            bases=(models.Model, passerelle.sms.SMSGatewayMixin),
29
            bases=(models.Model,),
30 30
        ),
31 31
    ]
passerelle/apps/orange/models.py
2 2
from django.utils.translation import ugettext_lazy as _
3 3
from django.db import models
4 4

  
5
from passerelle.base.models import BaseResource
6
from passerelle.sms import SMSGatewayMixin
5
from passerelle.base.models import BaseResource, SMSResource
7 6

  
8 7
from . import soap
9 8

  
10 9

  
11
class OrangeSMSGateway(BaseResource, SMSGatewayMixin):
10
class OrangeSMSGateway(BaseResource, SMSResource):
12 11
    keystore = models.FileField(upload_to='orange', blank=True, null=True,
13 12
                                verbose_name=_('Keystore'),
14 13
                                help_text=_('Certificate and private key in PEM format'))
passerelle/apps/ovh/migrations/0001_initial.py
32 32
                'db_table': 'sms_ovh',
33 33
                'verbose_name': 'OVH',
34 34
            },
35
            bases=(models.Model, passerelle.sms.SMSGatewayMixin),
35
            bases=(models.Model,),
36 36
        ),
37 37
    ]
passerelle/apps/ovh/models.py
5 5
from django.utils.encoding import force_text
6 6

  
7 7
from passerelle.utils.jsonresponse import APIError
8
from passerelle.base.models import BaseResource
9
from passerelle.sms import SMSGatewayMixin
8
from passerelle.base.models import BaseResource, SMSResource
10 9

  
11 10

  
12
class OVHSMSGateway(BaseResource, SMSGatewayMixin):
11
class OVHSMSGateway(BaseResource, SMSResource):
13 12
    URL = 'https://www.ovh.com/cgi-bin/sms/http2sms.cgi'
14 13
    MESSAGES_CLASSES = (
15 14
        (0, _('Message are directly shown to users on phone screen '
passerelle/apps/oxyd/migrations/0001_initial.py
28 28
                'db_table': 'sms_oxyd',
29 29
                'verbose_name': 'Oxyd',
30 30
            },
31
            bases=(models.Model, passerelle.sms.SMSGatewayMixin),
31
            bases=(models.Model,),
32 32
        ),
33 33
    ]
passerelle/apps/oxyd/models.py
4 4
from django.utils.encoding import force_text
5 5

  
6 6
from passerelle.utils.jsonresponse import APIError
7
from passerelle.base.models import BaseResource
8
from passerelle.sms import SMSGatewayMixin
7
from passerelle.base.models import BaseResource, SMSResource
9 8
from django.utils.translation import ugettext_lazy as _
10 9

  
11 10

  
12
class OxydSMSGateway(BaseResource, SMSGatewayMixin):
11
class OxydSMSGateway(BaseResource, SMSResource):
13 12
    username = models.CharField(verbose_name=_('Username'), max_length=64)
14 13
    password = models.CharField(verbose_name=_('Password'), max_length=64)
15 14
    default_country_code = models.CharField(verbose_name=_('Default country code'), max_length=3,
passerelle/base/models.py
37 37

  
38 38
import passerelle
39 39
import requests
40
from passerelle.compat import json_loads
40 41
from passerelle.utils.api import endpoint
41 42
from passerelle.utils.jsonresponse import APIError
42 43

  
......
899 900

  
900 901
    class Meta:
901 902
        abstract = True
903

  
904

  
905
class SMSResource(models.Model):
906
    category = _('SMS Providers')
907
    documentation_url = 'https://doc-publik.entrouvert.com/admin-fonctionnel/les-tutos/configuration-envoi-sms/'
908

  
909
    _can_send_messages_description = _('Sending messages is limited to the following API users:')
910

  
911
    @classmethod
912
    def clean_numbers(cls, destinations, default_country_code='33',
913
                      default_trunk_prefix='0'):  # Yeah France first !
914
        numbers = []
915
        for dest in destinations:
916
            # most gateways needs the number prefixed by the country code, this is
917
            # really unfortunate.
918
            dest = dest.strip()
919
            number = ''.join(re.findall('[0-9]', dest))
920
            if dest.startswith('+'):
921
                number = '00' + number
922
            elif number.startswith('00'):
923
                # assumes 00 is international access code, remove it
924
                pass
925
            elif number.startswith(default_trunk_prefix):
926
                number = '00' + default_country_code + number[len(default_trunk_prefix):]
927
            else:
928
                raise NotImplementedError('phone number %r is unsupported (no '
929
                                          'international prefix, no local '
930
                                          'trunk prefix)' % number)
931
            numbers.append(number)
932
        return numbers
933

  
934
    @endpoint(perm='can_send_messages', methods=['post'])
935
    def send(self, request, *args, **kwargs):
936
        try:
937
            data = json_loads(request.body)
938
            assert isinstance(data, dict), 'JSON payload is not a dict'
939
            assert 'message' in data, 'missing "message" in JSON payload'
940
            assert 'from' in data, 'missing "from" in JSON payload'
941
            assert 'to' in data, 'missing "to" in JSON payload'
942
            assert isinstance(data['message'], six.text_type), 'message is not a string'
943
            assert isinstance(data['from'], six.text_type), 'from is not a string'
944
            assert all(map(lambda x: isinstance(x, six.text_type), data['to'])), \
945
                'to is not a list of strings'
946
        except (ValueError, AssertionError) as e:
947
            raise APIError('Payload error: %s' % e)
948
        logging.info('sending message %r to %r with sending number %r',
949
                     data['message'], data['to'], data['from'])
950
        if 'nostop' in request.GET:
951
            return {'data': self.send_msg(data['message'], data['from'], data['to'], stop=False)}
952
        return {'data': self.send_msg(data['message'], data['from'], data['to'], stop=True)}
953

  
954
    class Meta:
955
        abstract = True
passerelle/sms/__init__.py
1
import logging
2
import re
3

  
4
from django.utils import six
5
from django.utils.translation import ugettext_lazy as _
6
from passerelle.compat import json_loads
7
from passerelle.utils.api import endpoint
8
from passerelle.utils.jsonresponse import APIError
9

  
10

  
11
class SMSGatewayMixin(object):
12
    category = _('SMS Providers')
13
    documentation_url = 'https://doc-publik.entrouvert.com/admin-fonctionnel/les-tutos/configuration-envoi-sms/'
14

  
15
    _can_send_messages_description = _('Sending messages is limited to the following API users:')
16

  
17
    @classmethod
18
    def clean_numbers(cls, destinations, default_country_code='33',
19
                      default_trunk_prefix='0'):  # Yeah France first !
20
        numbers = []
21
        for dest in destinations:
22
            # most gateways needs the number prefixed by the country code, this is
23
            # really unfortunate.
24
            dest = dest.strip()
25
            number = ''.join(re.findall('[0-9]', dest))
26
            if dest.startswith('+'):
27
                number = '00' + number
28
            elif number.startswith('00'):
29
                # assumes 00 is international access code, remove it
30
                pass
31
            elif number.startswith(default_trunk_prefix):
32
                number = '00' + default_country_code + number[len(default_trunk_prefix):]
33
            else:
34
                raise NotImplementedError('phone number %r is unsupported (no '
35
                                          'international prefix, no local '
36
                                          'trunk prefix)' % number)
37
            numbers.append(number)
38
        return numbers
39

  
40
    @endpoint(perm='can_send_messages', methods=['post'])
41
    def send(self, request, *args, **kwargs):
42
        try:
43
            data = json_loads(request.body)
44
            assert isinstance(data, dict), 'JSON payload is not a dict'
45
            assert 'message' in data, 'missing "message" in JSON payload'
46
            assert 'from' in data, 'missing "from" in JSON payload'
47
            assert 'to' in data, 'missing "to" in JSON payload'
48
            assert isinstance(data['message'], six.text_type), 'message is not a string'
49
            assert isinstance(data['from'], six.text_type), 'from is not a string'
50
            assert all(map(lambda x: isinstance(x, six.text_type), data['to'])), \
51
                'to is not a list of strings'
52
        except (ValueError, AssertionError) as e:
53
            raise APIError('Payload error: %s' % e)
54
        logging.info('sending message %r to %r with sending number %r',
55
                     data['message'], data['to'], data['from'])
56
        if 'nostop' in request.GET:
57
            return {'data': self.send_msg(data['message'], data['from'], data['to'], stop=False)}
58
        return {'data': self.send_msg(data['message'], data['from'], data['to'], stop=True)}
tests/test_sms.py
2 2

  
3 3
from django.contrib.contenttypes.models import ContentType
4 4

  
5
from passerelle.base.models import ApiUser, AccessRight
6
from passerelle.sms import SMSGatewayMixin
5
from passerelle.base.models import ApiUser, AccessRight, SMSResource
7 6

  
8 7
from test_manager import login, admin_user
9 8

  
......
11 10

  
12 11
pytestmark = pytest.mark.django_db
13 12

  
14
klasses = SMSGatewayMixin.__subclasses__()
13
klasses = SMSResource.__subclasses__()
15 14

  
16 15

  
17 16
def test_clean_numbers():
18
    assert SMSGatewayMixin.clean_numbers(['+ 33 12']) == ['003312']
19
    assert SMSGatewayMixin.clean_numbers(['0 0 33 12']) == ['003312']
20
    assert SMSGatewayMixin.clean_numbers(['0 12']) == ['003312']
21
    assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '32', '1') == ['003312']
22
    assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '32', '1') == ['003312']
23
    assert SMSGatewayMixin.clean_numbers(['1 12'], '32', '1') == ['003212']
17
    assert SMSResource.clean_numbers(['+ 33 12']) == ['003312']
18
    assert SMSResource.clean_numbers(['0 0 33 12']) == ['003312']
19
    assert SMSResource.clean_numbers(['0 12']) == ['003312']
20
    assert SMSResource.clean_numbers(['+ 33 12'], '32', '1') == ['003312']
21
    assert SMSResource.clean_numbers(['0 0 33 12'], '32', '1') == ['003312']
22
    assert SMSResource.clean_numbers(['1 12'], '32', '1') == ['003212']
24 23

  
25 24

  
26 25
@pytest.fixture(params=klasses)
27
-