Projet

Général

Profil

0001-clicrdv-replace-urllib2-by-connector-s-requests-7898.patch

Serghei Mihai (congés, retour 15/05), 13 janvier 2020 16:56

Télécharger (14,6 ko)

Voir les différences:

Subject: [PATCH] clicrdv: replace urllib2 by connector's requests (#7898)

 passerelle/apps/clicrdv/models.py |  56 ++++++--------
 tests/test_clicrdv.py             | 120 ++++++++++++++++++++++--------
 2 files changed, 111 insertions(+), 65 deletions(-)
passerelle/apps/clicrdv/models.py
7 7
import base64
8 8
import datetime
9 9
import json
10
import urllib2
10
import requests
11 11

  
12 12
from django.conf import settings
13 13
from django.core.urlresolvers import reverse
......
15 15
from django.utils.dateformat import format as date_format
16 16
from django.utils.dateformat import time_format
17 17
from django.utils.translation import ugettext_lazy as _
18
from django.utils.http import urlquote
18 19

  
19 20
from passerelle.base.models import BaseResource
20 21
from passerelle.utils.api import endpoint
......
48 49
    def get_verbose_name(cls):
49 50
        return cls._meta.verbose_name
50 51

  
51
    def get_request(self, uri):
52
    def request(self, uri, method='get', **kwargs):
52 53
        url = 'https://%s/api/v1/groups/%s/%s' % (self.server, self.group_id, uri)
53 54
        if '?' in url:
54 55
           url = url + '&apikey=%s&format=json' % self.apikey
55 56
        else:
56 57
           url = url + '?apikey=%s&format=json' % self.apikey
57
        req = urllib2.Request(url)
58
        authheader = 'Basic ' + \
59
            base64.encodestring('%s:%s' % (self.username, self.password))[:-1]
60
        req.add_header('Authorization', authheader)
61
        return req
62

  
63
    def get_json(self, uri):
64
        req = self.get_request(uri)
65
        return json.load(urllib2.urlopen(req))
58
        basic_auth = requests.auth.HTTPBasicAuth(self.username, self.password)
59
        return self.requests.request(method, url, auth=basic_auth, **kwargs)
66 60

  
67 61
    @endpoint(name='interventionsets')
68 62
    def get_interventionsets(self, request, **kwargs):
69
        records = self.get_json('interventionsets').get('records')
63
        response = self.request('interventionsets')
64
        records = response.json().get('records')
70 65
        records.sort(lambda x,y: cmp(x['sort'], y['sort']))
71 66
        ret = []
72 67
        for record in records:
......
77 72
    @endpoint(name='interventionsets', pattern='(?P<set>\d+)/')
78 73
    def get_interventions(self, request, set, **kwargs):
79 74
        ret = []
80
        records = self.get_json('interventions?interventionset_id=%s' % set).get('records')
75
        response = self.request('interventions?interventionset_id=%s' % set)
76
        records = response.json().get('records')
81 77
        records.sort(lambda x,y: cmp(x['sort'], y['sort']))
82 78
        for record in records:
83 79
            if record.get('publicname'):
......
93 89
        if date_end is None:
94 90
            date_end = (datetime.datetime.today() + datetime.timedelta(366)).strftime('%Y-%m-%d')
95 91
        if date_start:
96
            request_uri = request_uri + '&start=%s' % urllib2.quote(date_start)
92
            request_uri = request_uri + '&start=%s' % urlquote(date_start)
97 93
        if date_end:
98
            request_uri = request_uri + '&end=%s' % urllib2.quote(date_end)
99
        for timeslot in self.get_json(request_uri).get('availabletimeslots'):
94
            request_uri = request_uri + '&end=%s' % urlquote(date_end)
95
        for timeslot in self.request(request_uri).json().get('availabletimeslots'):
100 96
            timeslots.append(timeslot.get('start'))
101 97
        timeslots.sort()
102 98
        return timeslots
......
137 133
        times.sort(lambda x,y: cmp(x.get('id'), y.get('id')))
138 134
        return times
139 135

  
140
    def cancel(self, id, **kwargs):
141
        appointment_id = int(id)
142
        req = self.get_request('appointments/%s' % appointment_id)
143
        req.get_method = (lambda: 'DELETE')
136
    def cancel(self, appointment_id, **kwargs):
144 137
        try:
145
            fd = urllib2.urlopen(req)
146
            none = fd.read()
147
        except urllib2.HTTPError as e:
138
            r = self.request('DELETE', 'appointments/%s' % appointment_id)
139
            r.raise_for_status()
140
        except requests.exceptions.HTTPError as e:
148 141
            # clicrdv will return a "Bad Request" (HTTP 400) response
149 142
            # when it's not possible to remove an appointment
150 143
            # (for example because it's too late)
151
            response = e.read()
152
            response = json.loads(response)
153
            return {'success': False, 'error': response}
144
            response = json.loads(str(e))
145
            return {'success': False, 'error': response.get('error')}
154 146
        return {'success': True}
155 147

  
156 148

  
......
190 182
        for fieldname in (fields.keys() + extra.keys() + data.keys()):
191 183
            if fieldname.startswith('clicrdv_fiche_'):
192 184
                appointment['fiche'][fieldname[14:]] = get_data(fieldname) or ''
193
        req = self.get_request('appointments')
194
        req.add_data(json.dumps({'appointment': appointment}))
195
        req.add_header('Content-Type', 'application/json')
196 185
        try:
197
            fd = urllib2.urlopen(req)
198
        except urllib2.HTTPError, e:
186
            r = self.request('appointments', 'post', json=appointment)
187
            r.raise_for_status()
188
        except requests.exceptions.HTTPError as e:
199 189
            try:
200
                error = json.load(e.fp)[0].get('error')
190
                error = json.loads(str(e))[0].get('error')
201 191
            except:
202 192
                error = 'Unknown error (Passerelle)'
203 193
            return {
......
206 196
            }
207 197
        else:
208 198
            success = True
209
            response = json.load(fd)
199
            response = r.json()
210 200
            appointment_id = response.get('records')[0].get('id')
211 201
            return {
212 202
                'success': True,
tests/test_clicrdv.py
1 1
import mock
2 2
import pytest
3 3
import urlparse
4
from StringIO import StringIO
4
from requests.exceptions import HTTPError
5

  
6
from django.contrib.contenttypes.models import ContentType
5 7

  
6 8
from passerelle.base.models import ApiUser, AccessRight
7 9
from passerelle.apps.clicrdv.models import ClicRdv
......
13 15
                        password='test')
14 16

  
15 17

  
16
@mock.patch('urllib2.urlopen')
17
def test_urlopen_call(urlopen, app, connector):
18
    urlopen.return_value = StringIO('"foo"')
19
    rjson = connector.get_json('bar')
20
    assert urlopen.call_count == 1
21
    req = urlopen.call_args[0][0]
22
    assert req.get_full_url().startswith('https://sandbox.clicrdv.com/api/v1/groups/5242/bar')
18
@mock.patch('passerelle.utils.Request.request')
19
def test_request_call(mocked_request, app, connector):
20
    mocked_request.json.return_value = "foo"
21
    connector.request('bar')
22
    assert mocked_request.call_count == 1
23
    req = mocked_request.call_args[0][1]
24
    assert req.startswith('https://sandbox.clicrdv.com/api/v1/groups/5242/bar')
23 25

  
24 26

  
25
@mock.patch('passerelle.apps.clicrdv.models.ClicRdv.get_json')
26
def test_interventionsets(mocked_get, app, connector):
27
    mocked_get.return_value = {
27
@mock.patch('passerelle.utils.Request.request')
28
def test_interventionsets(mocked_request, app, connector):
29
    response = mock.Mock()
30
    response.json.return_value = {
28 31
            "totalRecords": 2,
29 32
            "records": [
30 33
                {
......
42 45
                    "group_id": 5242,
43 46
                },
44 47
            ]}
48
    mocked_request.return_value = response
45 49
    resp = app.get('/clicrdv/test/interventionsets/')
46 50
    assert len(resp.json.get('data')) == 2
47 51
    assert resp.json.get('data')[0]['text'] == 'Une Demande de Passeport'
48 52

  
49 53

  
50
@mock.patch('passerelle.apps.clicrdv.models.ClicRdv.get_json')
51
def test_interventionsets_details(mocked_get, app, connector):
52
    mocked_get.return_value = {
54
@mock.patch('passerelle.utils.Request.request')
55
def test_interventionsets_details(mocked_request, app, connector):
56
    response = mock.Mock()
57
    response.json.return_value = {
53 58
            "totalRecords": 2,
54 59
            "records": [
55 60
                {
......
73 78
                    "abbr": "2 demandes"
74 79
                },
75 80
            ]}
76

  
81
    mocked_request.return_value = response
77 82
    resp = app.get('/clicrdv/test/interventionsets/7032/')
78 83
    assert len(resp.json.get('data')) == 2
79 84
    assert resp.json.get('data')[0]['text'] == 'pour une personne'
80 85

  
81
@mock.patch('urllib2.urlopen')
82
def test_interventions_get_datetimes(urlopen, app, connector):
83
    urlopen.return_value = StringIO('{"availabletimeslots": []}')
86
@mock.patch('passerelle.utils.Request.request')
87
def test_interventions_get_datetimes(mocked_request, app, connector):
88
    response = mock.Mock()
89
    response.json.return_value = {"availabletimeslots": []}
90
    mocked_request.return_value = response
84 91
    resp = app.get('/clicrdv/test/interventions/63258/dates/')
85 92
    assert resp.json.get('data') == []
86 93
    assert resp.json.get('err') == 0
87
    assert urlopen.call_count == 1
88
    url = urlopen.call_args[0][0].get_full_url()
94
    assert mocked_request.call_count == 1
95
    url = mocked_request.call_args[0][1]
89 96
    # https://sandbox.clicrdv.com/api/v1/groups/5242/availabletimeslots?
90 97
    #   intervention_ids[]=63258&start=2016-09-21&end=2017-09-22&apikey=test&format=json
91 98
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
......
101 108
    assert query['apikey'] == ['test']
102 109
    assert query['format'] == ['json']
103 110

  
104
    urlopen.return_value = StringIO('''{"availabletimeslots": [
105
            { "start": "2016-09-21 12:34:56" },
106
            { "start": "2016-09-22 11:22:33" }
107
        ]}''')
111
    response.json.return_value = {"availabletimeslots": [
112
        { "start": "2016-09-21 12:34:56" },
113
        { "start": "2016-09-22 11:22:33" }
114
    ]}
115
    mocked_request.return_value = response
108 116
    resp = app.get('/clicrdv/test/interventions/63258/dates/').json
109
    assert urlopen.call_count == 2
117
    assert mocked_request.call_count == 2
110 118
    assert resp.get('err') == 0
111 119
    assert len(resp.get('data')) == 2
112 120
    assert resp['data'][0] == {'id': '2016-09-21', 'text': '21 September 2016'}
113 121
    assert resp['data'][1] == {'id': '2016-09-22', 'text': '22 September 2016'}
114 122

  
115
    urlopen.return_value = StringIO('''{"availabletimeslots": [
123
    response.json.return_value = {"availabletimeslots": [
116 124
            { "start": "2016-09-22 11:22:33" },
117 125
            { "start": "2016-09-21 12:34:56" }
118
        ]}''') # will be sorted
126
    ]} # will be sorted
127
    mocked_request.return_value = response
119 128
    resp = app.get('/clicrdv/test/interventions/63258/datetimes/').json
120
    assert urlopen.call_count == 3
129
    assert mocked_request.call_count == 3
121 130
    assert resp.get('err') == 0
122 131
    assert len(resp.get('data')) == 2
123 132
    assert resp['data'][0] == {'id': '2016-09-21-12:34:56', 'text': '21 September 2016 12:34'}
124 133
    assert resp['data'][1] == {'id': '2016-09-22-11:22:33', 'text': '22 September 2016 11:22'}
125 134

  
126
    urlopen.return_value = StringIO('''{"availabletimeslots": [
135
    response.json.return_value = {"availabletimeslots": [
127 136
            { "start": "2016-09-21 12:34:56" },
128 137
            { "start": "2016-09-21 11:22:33" }
129
        ]}''') # will be sorted
138
    ]} # will be sorted
139
    mocked_request.return_value = response
130 140
    resp = app.get('/clicrdv/test/interventions/63258/2016-09-21/times').json
131
    assert urlopen.call_count == 4
132
    url = urlopen.call_args[0][0].get_full_url()
141
    assert mocked_request.call_count == 4
142
    url = mocked_request.call_args[0][1]
133 143
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
134 144
    query = urlparse.parse_qs(query, keep_blank_values=True)
135 145
    assert query['start'] == ['2016-09-21 00:00:00']
......
138 148
    assert len(resp.get('data')) == 2
139 149
    assert resp['data'][0] == {'id': '11:22:33', 'text': '11:22'}
140 150
    assert resp['data'][1] == {'id': '12:34:56', 'text': '12:34'}
151

  
152
@mock.patch('passerelle.utils.Request.request')
153
def test_cancel_appointment(mocked_request, app, connector):
154
    obj_type = ContentType.objects.get_for_model(ClicRdv)
155
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API',
156
                                     key='apiuser')
157
    AccessRight.objects.create(codename='can_manage_appointment',
158
                resource_type=obj_type, resource_pk=connector.pk,
159
                apiuser=apiuser)
160

  
161
    resp = app.get('/clicrdv/test/63258/cancel?apikey=apiuser').json
162
    assert mocked_request.call_count == 1
163
    assert resp['data']['success']
164

  
165
@mock.patch('passerelle.utils.Request.request',
166
            side_effect=HTTPError('{"error": 1}'))
167
def test_failed_cancel_appointment(mocked_request, app, connector):
168
    obj_type = ContentType.objects.get_for_model(ClicRdv)
169
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API',
170
                                key='apiuser')
171
    AccessRight.objects.create(codename='can_manage_appointment',
172
                resource_type=obj_type, resource_pk=connector.pk,
173
                apiuser=apiuser)
174
    resp = app.get('/clicrdv/test/63258/cancel?apikey=apiuser').json
175
    assert mocked_request.call_count == 1
176
    assert resp.get('err') == 0
177
    assert resp['data']
178
    assert resp['data']['error'] == 1
179

  
180

  
181
@mock.patch('passerelle.utils.Request.request',
182
            side_effect=HTTPError('[{"error": "creation failed"}]'))
183
def test_failed_appointment_creation(mocked_request, app, connector):
184
    obj_type = ContentType.objects.get_for_model(ClicRdv)
185
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API',
186
                                key='apiuser')
187
    AccessRight.objects.create(codename='can_manage_appointment',
188
                resource_type=obj_type, resource_pk=connector.pk,
189
                apiuser=apiuser)
190

  
191
    data = {'fields': {'clicrdv_date_raw': '2017-01-01' , 'clicrdv_time_raw': '12:00:00',
192
                       'firstname': 'Foo', 'lastname': 'Bar', 'email': 'foobar@example.com'}}
193
    resp = app.post_json('/clicrdv/test/interventions/63258/create?apikey=apiuser', params=data).json
194
    assert resp['data']
195
    assert not resp['data']['success']
196
    assert resp['data']['error'] == 'creation failed'
141
-