Projet

Général

Profil

0003-sms-upgrade-orange-connector-to-use-zeep-library-369.patch

Nicolas Roche, 29 octobre 2019 21:44

Télécharger (13,8 ko)

Voir les différences:

Subject: [PATCH 3/3] sms: upgrade orange connector to use zeep library
 (#36921)

 passerelle/apps/orange/models.py              | 81 ++++++++++++++++--
 passerelle/apps/orange/soap.py                | 84 -------------------
 .../data/orange_sendAdvancedMessage_query.xml | 41 +++++----
 tests/test_sms.py                             | 11 ++-
 4 files changed, 99 insertions(+), 118 deletions(-)
 delete mode 100644 passerelle/apps/orange/soap.py
passerelle/apps/orange/models.py
1
from StringIO import StringIO
1
from requests.exceptions import ConnectionError
2 2

  
3
from django.core.files import File
4 3
from django.utils.translation import ugettext_lazy as _
5 4
from django.db import models
6 5

  
7 6
from passerelle.base.models import BaseResource
8 7
from passerelle.sms import SMSGatewayMixin
9

  
10
from . import soap
8
from passerelle.utils.jsonresponse import APIError
9
from passerelle.xml_builder import XmlBuilder
11 10

  
12 11

  
13 12
class OrangeSMSGateway(BaseResource, SMSGatewayMixin):
......
17 16
    default_country_code = '33'
18 17

  
19 18
    URL = ('https://www.api-contact-everyone.fr.orange-business.com/ContactEveryone/services'
20
           '/MultiDiffusionWS')
19
           '/MultiDiffusionWS?wsdl')
21 20

  
22 21
    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
23 22

  
......
29 28
        """Send a SMS using the Orange provider"""
30 29
        # unfortunately it lacks a batch API...
31 30
        destinations = self.clean_numbers(destinations, self.default_country_code)
32
        return soap.ContactEveryoneSoap(instance=self).send_advanced_message(destinations, sender,
33
                                                                             text)
31
        return self.send_advanced_message(destinations, sender, text)
32

  
33
    class ProfileListBuilder(XmlBuilder):
34
        schema = (
35
            'PROFILE_LIST',
36
            ('?loop', 'recipients',
37
             ('PROFILE',
38
              ('DEST_NAME', 'name_{to}'),
39
              ('DEST_FORENAME', 'forename_{to}'),
40
              ('DEST_ID', 'ID_{to}'),
41
              ('TERMINAL_GROUP',
42
               ('TERMINAL',
43
                ('TERMINAL_NAME', 'mobile1'),
44
                ('TERMINAL_ADDR', '{to}'),
45
                ('MEDIA_TYPE_GROUP',
46
                 ('MEDIA_TYPE', 'sms')))))))
47
        encoding = 'latin1'
48

  
49
    def get_client(self):
50
        try:
51
            soap_client = self.soap_client(wsdl_url=self.URL)
52
        except ConnectionError as e:
53
            raise APIError('Orange error: WSDL retrieval failed, %s' % e)
54
        return soap_client
55

  
56
    def send_message(self, recipients, content):
57
        client = self.get_client()
58
        message_type = client.get_type('ns0:WSMessage')
59
        message = message_type()
60
        message.fullContenu = True
61
        message.content = content
62
        message.subject = content
63
        message.resumeContent = content
64
        message.strategy = 'sms'
65
        send_profiles = self.ProfileListBuilder().string(
66
            context={'recipients': [{'to': to} for to in recipients]})
67
        message.sendProfiles = send_profiles
68
        try:
69
            resp = client.service.sendMessage(message)
70
        except Exception as e:
71
            raise APIError('Orange error: %s' % e)
72
        else:
73
            return {'msg_id': resp}
74

  
75
    def send_advanced_message(self, recipients, sender, content):
76
        client = self.get_client()
77
        message_type = client.get_type('ns0:WSAdvancedMessage')
78
        message = message_type()
79
        message.fullContenu = True
80
        message.content = content
81
        message.subject = content
82
        message.resumeContent = content
83
        message.strategy = 'sms'
84
        message.smsReplyTo = sender
85
        send_profiles = self.ProfileListBuilder().string(
86
            context={'recipients': [{'to': to} for to in recipients]})
87
        message.sendProfiles = send_profiles
88
        # required booleans
89
        message.validFaxReplyTo = False
90
        message.validFormatMailReplyTo = False
91
        message.validMaxStartCall = False
92
        message.validMinStartCall = False
93
        message.validSmsReplyTo = False
94
        message.validTelReplyTo = False
95
        try:
96
            resp = client.service.sendAdvancedMessage(message)
97
        except Exception as e:
98
            raise APIError('Orange error: %s' % e)
99
        else:
100
            return {'msg_id': resp}
passerelle/apps/orange/soap.py
1
import datetime
2
import os.path
3

  
4

  
5
from passerelle.utils.jsonresponse import APIError
6
from passerelle.soap import Soap
7
from passerelle.xml_builder import XmlBuilder
8

  
9

  
10
class ContactEveryoneSoap(Soap):
11
    WSDL_URL = ('https://www.api-contact-everyone.fr.orange-business.com/'
12
                'ContactEveryone/services/MultiDiffusionWS?wsdl')
13
    ORANGE_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'orange.pem')
14

  
15
    url = WSDL_URL
16

  
17
    class ProfileListBuilder(XmlBuilder):
18
        schema = (
19
            'PROFILE_LIST',
20
            ('?loop', 'recipients',
21
             ('PROFILE',
22
              ('DEST_NAME', 'name_{to}'),
23
              ('DEST_FORENAME', 'forename_{to}'),
24
              ('DEST_ID', 'ID_{to}'),
25
              ('TERMINAL_GROUP',
26
               ('TERMINAL',
27
                ('TERMINAL_NAME', 'mobile1'),
28
                ('TERMINAL_ADDR', '{to}'),
29
                ('MEDIA_TYPE_GROUP',
30
                 ('MEDIA_TYPE', 'sms')))))))
31
        encoding = 'latin1'
32

  
33
    @property
34
    def verify(self):
35
        # Do not break if certificate is not updated
36
        if datetime.datetime.now() < datetime.datetime(2016, 11, 5):
37
            return self.ORANGE_CERTIFICATE
38
        else:
39
            return False
40

  
41
    def send_message(self, recipients, content):
42
        try:
43
            client = self.get_client()
44
        except Exception as e:
45
            raise APIError('Orange error: WSDL retrieval failed, %s' % e)
46
        message = client.factory.create('WSMessage')
47
        message.fullContenu = True
48
        message.content = content
49
        message.subject = content
50
        message.resumeContent = content
51
        message.strategy = 'sms'
52
        send_profiles = self.ProfileListBuilder().string(
53
            context={'recipients': [{'to': to} for to in recipients]})
54
        message.sendProfiles = send_profiles
55
        try:
56
            resp = client.service.sendMessage(message)
57
        except Exception as e:
58
            raise APIError('Orange error: %s' % e)
59
        else:
60
            return {'msg_ids': [msg_id for msg_id in resp.msgId]}
61

  
62
    def send_advanced_message(self, recipients, sender, content):
63
        try:
64
            client = self.get_client()
65
        except Exception as e:
66
            raise APIError('Orange error: WSDL retrieval failed, %s' % e)
67
        message = client.factory.create('WSAdvancedMessage')
68
        message.fullContenu = True
69
        message.content = content
70
        message.subject = content
71
        message.resumeContent = content
72
        message.strategy = 'sms'
73
        message.smsReplyTo = sender
74
        send_profiles = self.ProfileListBuilder().string(
75
            context={'recipients': [{'to': to} for to in recipients]})
76
        message.sendProfiles = send_profiles
77
        try:
78
            resp = client.service.sendAdvancedMessage(message)
79
        except Exception as e:
80
            raise APIError('Orange error: %s' % e)
81
        else:
82
            if isinstance(resp.msgId, list):
83
                return {'msg_ids': [msg_id for msg_id in resp.msgId]}
84
            return {'msg_ids': [resp.msgId]}
tests/data/orange_sendAdvancedMessage_query.xml
1
<SOAP-ENV:Envelope xmlns:ns0="MultiDiffusionWS" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
2
  <SOAP-ENV:Header/>
3
  <ns1:Body>
4
    <ns0:sendAdvancedMessage>
1
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
2
  <soap-env:Body>
3
    <ns0:sendAdvancedMessage xmlns:ns0="MultiDiffusionWS">
5 4
      <ns0:wsAdvancedMessage>
6 5
        <ns0:content>hello</ns0:content>
7
        <ns0:custId xsi:nil="true"/>
8
        <ns0:faxReplyTo xsi:nil="true"/>
9
        <ns0:from xsi:nil="true"/>
6
        <ns0:custId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
7
        <ns0:faxReplyTo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
8
        <ns0:from xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
10 9
        <ns0:fullContenu>true</ns0:fullContenu>
11
        <ns0:mailReplyTo xsi:nil="true"/>
12
        <ns0:orgName xsi:nil="true"/>
10
        <ns0:mailReplyTo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
11
        <ns0:orgName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
13 12
        <ns0:resumeContent>hello</ns0:resumeContent>
14 13
        <ns0:sendProfiles>&lt;PROFILE_LIST&gt;&lt;PROFILE&gt;&lt;DEST_NAME&gt;name_0033688888888&lt;/DEST_NAME&gt;&lt;DEST_FORENAME&gt;forename_0033688888888&lt;/DEST_FORENAME&gt;&lt;DEST_ID&gt;ID_0033688888888&lt;/DEST_ID&gt;&lt;TERMINAL_GROUP&gt;&lt;TERMINAL&gt;&lt;TERMINAL_NAME&gt;mobile1&lt;/TERMINAL_NAME&gt;&lt;TERMINAL_ADDR&gt;0033688888888&lt;/TERMINAL_ADDR&gt;&lt;MEDIA_TYPE_GROUP&gt;&lt;MEDIA_TYPE&gt;sms&lt;/MEDIA_TYPE&gt;&lt;/MEDIA_TYPE_GROUP&gt;&lt;/TERMINAL&gt;&lt;/TERMINAL_GROUP&gt;&lt;/PROFILE&gt;&lt;PROFILE&gt;&lt;DEST_NAME&gt;name_0033677777777&lt;/DEST_NAME&gt;&lt;DEST_FORENAME&gt;forename_0033677777777&lt;/DEST_FORENAME&gt;&lt;DEST_ID&gt;ID_0033677777777&lt;/DEST_ID&gt;&lt;TERMINAL_GROUP&gt;&lt;TERMINAL&gt;&lt;TERMINAL_NAME&gt;mobile1&lt;/TERMINAL_NAME&gt;&lt;TERMINAL_ADDR&gt;0033677777777&lt;/TERMINAL_ADDR&gt;&lt;MEDIA_TYPE_GROUP&gt;&lt;MEDIA_TYPE&gt;sms&lt;/MEDIA_TYPE&gt;&lt;/MEDIA_TYPE_GROUP&gt;&lt;/TERMINAL&gt;&lt;/TERMINAL_GROUP&gt;&lt;/PROFILE&gt;&lt;/PROFILE_LIST&gt;</ns0:sendProfiles>
15 14
        <ns0:smsReplyTo>+33699999999</ns0:smsReplyTo>
16
        <ns0:startCall xsi:nil="true"/>
15
        <ns0:startCall xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
17 16
        <ns0:strategy>sms</ns0:strategy>
18 17
        <ns0:subject>hello</ns0:subject>
19
        <ns0:telReplyTo xsi:nil="true"/>
20
        <ns0:to xsi:nil="true"/>
21
        <ns0:validFaxReplyTo/>
22
        <ns0:validFormatMailReplyTo/>
23
        <ns0:validMaxStartCall/>
24
        <ns0:validMinStartCall/>
25
        <ns0:validSmsReplyTo/>
26
        <ns0:validTelReplyTo/>
27
        <ns0:validityPeriod xsi:nil="true"/>
18
        <ns0:telReplyTo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
19
        <ns0:to xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
20
        <ns0:validFaxReplyTo>false</ns0:validFaxReplyTo>
21
        <ns0:validFormatMailReplyTo>false</ns0:validFormatMailReplyTo>
22
        <ns0:validMaxStartCall>false</ns0:validMaxStartCall>
23
        <ns0:validMinStartCall>false</ns0:validMinStartCall>
24
        <ns0:validSmsReplyTo>false</ns0:validSmsReplyTo>
25
        <ns0:validTelReplyTo>false</ns0:validTelReplyTo>
26
        <ns0:validityPeriod xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
28 27
      </ns0:wsAdvancedMessage>
29 28
    </ns0:sendAdvancedMessage>
30
  </ns1:Body>
31
</SOAP-ENV:Envelope>
29
  </soap-env:Body>
30
</soap-env:Envelope>
tests/test_sms.py
9 9
from django.core.urlresolvers import reverse
10 10

  
11 11
from passerelle.apps.orange.models import OrangeSMSGateway
12
from passerelle.apps.orange.soap import ContactEveryoneSoap
13 12
from passerelle.base.models import ApiUser, AccessRight
14 13
from passerelle.sms import SMSGatewayMixin
15 14

  
......
97 96
    assert 'accessright/add' in resp.body
98 97

  
99 98

  
100
@mock.patch('passerelle.soap.requests.get')
101
@mock.patch('passerelle.soap.requests.post')
99
@mock.patch('passerelle.utils.Request.get')
100
@mock.patch('passerelle.utils.Request.post')
102 101
def test_orange_connector(mocked_post, mocked_get, app, orange_conn):
103 102

  
104 103
    def orange_mocked_get(url, params=None, **kwargs):
105
        assert url == ContactEveryoneSoap.WSDL_URL
104
        assert url == OrangeSMSGateway.URL
106 105
        target_file = os.path.join(os.path.dirname(__file__), 'data', 'orange.wsdl')
107 106
        response = Response()
108 107
        response._content=file(target_file).read()
......
114 113
            os.path.dirname(__file__), 'data', 'orange_sendAdvancedMessage_query.xml')
115 114
        xml1 = etree.parse(xml_sent_expeted)
116 115
        xml2 = etree.fromstring(kwargs['data'])
117
        if (url not in ContactEveryoneSoap.WSDL_URL
116
        if (url not in OrangeSMSGateway.URL
118 117
            or etree.tostring(xml1, pretty_print=True)
119 118
            != etree.tostring(xml2, pretty_print=True)):
120 119
            # expected value may be updated here
......
153 152
    mocked_post.side_effect=orange_mocked_post
154 153
    resp = app.post_json(url, params=payload)
155 154
    assert resp.json['err'] == 0
156
    assert resp.json['data']['msg_ids'] == ['msg-123']
155
    assert resp.json['data']['msg_id'] == 'msg-123'
157
-