Projet

Général

Profil

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

Serghei Mihai (congés, retour 15/05), 24 janvier 2017 12:36

Télécharger (14,5 ko)

Voir les différences:

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

 passerelle/apps/clicrdv/models.py |  57 ++++++++----------
 tests/test_clicrdv.py             | 118 ++++++++++++++++++++++++++++----------
 2 files changed, 110 insertions(+), 65 deletions(-)
passerelle/apps/clicrdv/models.py
8 8
import datetime
9 9
import json
10 10
import urllib2
11
import requests
11 12

  
12 13
from django.conf import settings
13 14
from django.core.urlresolvers import reverse
......
50 51
    def get_icon_class(cls):
51 52
        return 'clock'
52 53

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

  
65
    def get_json(self, uri):
66
        req = self.get_request(uri)
67
        return json.load(urllib2.urlopen(req))
60
        basic_auth = requests.auth.HTTPBasicAuth(self.username, self.password)
61
        return self.requests.request(method, url, auth=basic_auth, **kwargs)
68 62

  
69 63
    @endpoint(name='interventionsets', serializer_type='json-api')
70 64
    def get_interventionsets(self, request, **kwargs):
71
        records = self.get_json('interventionsets').get('records')
65
        response = self.request('interventionsets')
66
        records = response.json().get('records')
72 67
        records.sort(lambda x,y: cmp(x['sort'], y['sort']))
73 68
        ret = []
74 69
        for record in records:
......
79 74
    @endpoint(name='interventionsets', pattern='(?P<set>\d+)/', serializer_type='json-api')
80 75
    def get_interventions(self, request, set, **kwargs):
81 76
        ret = []
82
        records = self.get_json('interventions?interventionset_id=%s' % set).get('records')
77
        response = self.request('interventions?interventionset_id=%s' % set)
78
        records = response.json().get('records')
83 79
        records.sort(lambda x,y: cmp(x['sort'], y['sort']))
84 80
        for record in records:
85 81
            if record.get('publicname'):
......
98 94
            request_uri = request_uri + '&start=%s' % urllib2.quote(date_start)
99 95
        if date_end:
100 96
            request_uri = request_uri + '&end=%s' % urllib2.quote(date_end)
101
        for timeslot in self.get_json(request_uri).get('availabletimeslots'):
97
        for timeslot in self.request(request_uri).json().get('availabletimeslots'):
102 98
            timeslots.append(timeslot.get('start'))
103 99
        timeslots.sort()
104 100
        return timeslots
......
139 135
        times.sort(lambda x,y: cmp(x.get('id'), y.get('id')))
140 136
        return times
141 137

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

  
158 150

  
......
192 184
        for fieldname in (fields.keys() + extra.keys() + data.keys()):
193 185
            if fieldname.startswith('clicrdv_fiche_'):
194 186
                appointment['fiche'][fieldname[14:]] = get_data(fieldname) or ''
195
        req = self.get_request('appointments')
196
        req.add_data(json.dumps({'appointment': appointment}))
197
        req.add_header('Content-Type', 'application/json')
187
        data = json.dumps({'appointment': appointment})
198 188
        try:
199
            fd = urllib2.urlopen(req)
200
        except urllib2.HTTPError, e:
189
            r = self.request('appointments', method='post', data=data,
190
                    headers={'Content-Type': 'application/json'})
191
            r.raise_for_status()
192
        except requests.exceptions.HTTPError as e:
201 193
            try:
202
                error = json.load(e.fp)[0].get('error')
194
                error = json.loads(str(e))[0].get('error')
203 195
            except:
204 196
                error = 'Unknown error (Passerelle)'
205
            return {
206
                'success': False,
207
                'error': error,
208
            }
197
            return {'success': False, 'error': error}
209 198
        else:
210 199
            success = True
211
            response = json.load(fd)
200
            response = r.json()
212 201
            appointment_id = response.get('records')[0].get('id')
213 202
            return {
214 203
                '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 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.LoggedRequest.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('clicrdv.models.ClicRdv.get_json')
26
def test_interventionsets(mocked_get, app, connector):
27
    mocked_get.return_value = {
27
@mock.patch('passerelle.utils.LoggedRequest.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('clicrdv.models.ClicRdv.get_json')
51
def test_interventionsets_details(mocked_get, app, connector):
52
    mocked_get.return_value = {
54
@mock.patch('passerelle.utils.LoggedRequest.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
                {
......
64 69
                    },
65 70
                {
66 71
                    "sort": 2,
67
                    "publicname": "pour deuxs personnes",
72
                    "publicname": "pour deux personnes",
68 73
                    "description": None,
69 74
                    "name": "2 personnes",
70 75
                    "interventionset_id": 7032,
......
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.LoggedRequest.request')
87
def test_interventions_get_datetimes(mocked_request, app, connector):
88
    mock_response = mock.Mock()
89
    mock_response.json.return_value = {"availabletimeslots": []}
90
    mocked_request.return_value = mock_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": [
111
    mock_response.json.return_value = {"availabletimeslots": [
105 112
            { "start": "2016-09-21 12:34:56" },
106 113
            { "start": "2016-09-22 11:22:33" }
107
        ]}''')
114
    ]}
115
    mocked_request.return_value = mock_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
    mock_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

  
128
    mocked_request.return_value = mock_response
119 129
    resp = app.get('/clicrdv/test/interventions/63258/datetimes/').json
120
    assert urlopen.call_count == 3
130
    assert mocked_request.call_count == 3
121 131
    assert resp.get('err') == 0
122 132
    assert len(resp.get('data')) == 2
123 133
    assert resp['data'][0] == {'id': '2016-09-21-12:34:56', 'text': '21 September 2016 12:34'}
124 134
    assert resp['data'][1] == {'id': '2016-09-22-11:22:33', 'text': '22 September 2016 11:22'}
125 135

  
126
    urlopen.return_value = StringIO('''{"availabletimeslots": [
136
    mock_response.json.return_value = {"availabletimeslots": [
127 137
            { "start": "2016-09-21 12:34:56" },
128 138
            { "start": "2016-09-21 11:22:33" }
129
        ]}''') # will be sorted
139
    ]} # will be sorted
140

  
141
    mocked_request.return_value = mock_response
130 142
    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()
143
    assert mocked_request.call_count == 4
144
    url = mocked_request.call_args[0][1]
133 145
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
134 146
    query = urlparse.parse_qs(query, keep_blank_values=True)
135 147
    assert query['start'] == ['2016-09-21 00:00:00']
......
138 150
    assert len(resp.get('data')) == 2
139 151
    assert resp['data'][0] == {'id': '11:22:33', 'text': '11:22'}
140 152
    assert resp['data'][1] == {'id': '12:34:56', 'text': '12:34'}
153

  
154
@mock.patch('passerelle.utils.LoggedRequest.request')
155
def test_cancel_appointment(mocked_request, app, connector):
156
    obj_type = ContentType.objects.get_for_model(ClicRdv)
157
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API',
158
                                     key='apiuser')
159
    AccessRight.objects.create(codename='can_manage_appointment',
160
                resource_type=obj_type, resource_pk=connector.pk,
161
                apiuser=apiuser)
162
    resp = app.get('/clicrdv/test/63258/cancel?apikey=apiuser').json
163
    assert mocked_request.call_count == 1
164
    assert resp['data']['success']
165

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

  
181

  
182
@mock.patch('passerelle.utils.LoggedRequest.request',
183
            side_effect=HTTPError('[{"error": "creation failed"}]'))
184
def test_failed_appointment_creation(mocked_request, app, connector):
185
    obj_type = ContentType.objects.get_for_model(ClicRdv)
186
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API',
187
                                key='apiuser')
188
    AccessRight.objects.create(codename='can_manage_appointment',
189
                resource_type=obj_type, resource_pk=connector.pk,
190
                apiuser=apiuser)
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', data).json
194
    assert resp['data']
195
    assert not resp['data']['success']
196
    assert resp['data']['error'] == 'creation failed'
141
-