Projet

Général

Profil

0001-clicrdv-replace-urllib2-by-connectors-requests-7898.patch

Serghei Mihai (congés, retour 15/05), 29 décembre 2016 14:17

Télécharger (11,6 ko)

Voir les différences:

Subject: [PATCH] clicrdv: replace urllib2 by connectors requests (#7898)

 passerelle/apps/clicrdv/models.py | 47 ++++++++------------
 tests/test_clicrdv.py             | 91 ++++++++++++++++++++++++++++-----------
 2 files changed, 83 insertions(+), 55 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
......
18 19

  
19 20
from passerelle.base.models import BaseResource
20 21
from passerelle.utils.api import endpoint
22
from passerelle.utils.jsonresponse import APIError
21 23

  
22 24
CLICRDV_SERVERS = (
23 25
    ('www.clicrdv.com', 'Production (www.clicrdv.com)'),
......
50 52
    def get_icon_class(cls):
51 53
        return 'clock'
52 54

  
53
    def get_request(self, uri):
55
    def request(self, uri, method='get', **kwargs):
54 56
        url = 'https://%s/api/v1/groups/%s/%s' % (self.server, self.group_id, uri)
55 57
        if '?' in url:
56 58
           url = url + '&apikey=%s&format=json' % self.apikey
57 59
        else:
58 60
           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
61
        basic_auth = requests.auth.HTTPBasicAuth(self.username, self.password)
62
        return self.requests.request(method, url, auth=basic_auth, **kwargs)
64 63

  
65 64
    def get_json(self, uri):
66
        req = self.get_request(uri)
67
        return json.load(urllib2.urlopen(req))
65
        response = self.request(uri)
66
        return response.json()
68 67

  
69 68
    @endpoint(name='interventionsets', serializer_type='json-api')
70 69
    def get_interventionsets(self, request, **kwargs):
......
139 138
        times.sort(lambda x,y: cmp(x.get('id'), y.get('id')))
140 139
        return times
141 140

  
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')
141
    def cancel(self, appointment_id, **kwargs):
146 142
        try:
147
            fd = urllib2.urlopen(req)
148
            none = fd.read()
149
        except urllib2.HTTPError as e:
143
            r = self.request('delete', 'appointments/%s' % appointment_id)
144
        except requests.exceptions.HTTPError as e:
150 145
            # clicrdv will return a "Bad Request" (HTTP 400) response
151 146
            # when it's not possible to remove an appointment
152 147
            # (for example because it's too late)
153
            response = e.read()
154
            response = json.loads(response)
155
            return {'success': False, 'error': response}
148
            raise APIError('ClicRDV error: %s' % e)
156 149
        return {'success': True}
157 150

  
158 151

  
......
192 185
        for fieldname in (fields.keys() + extra.keys() + data.keys()):
193 186
            if fieldname.startswith('clicrdv_fiche_'):
194 187
                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')
188
        data = json.dumps({'appointment': appointment})
198 189
        try:
199
            fd = urllib2.urlopen(req)
200
        except urllib2.HTTPError, e:
190
            r = self.request('appointments', method='post', data=data,
191
                    headers={'Content-Type', 'application/json'})
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)).get('error')
203 195
            except:
204 196
                error = 'Unknown error (Passerelle)'
205
            return {
206
                'success': False,
207
                'error': error,
208
            }
197
            raise APIError('ClicRDV error: %s' % 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"')
18
@mock.patch('passerelle.utils.LoggedRequest.request')
19
def test_urlopen_call(mocked_request, app, connector):
20
    mocked_request.json.return_value = "foo"
19 21
    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')
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 27
@mock.patch('clicrdv.models.ClicRdv.get_json')
26
def test_interventionsets(mocked_get, app, connector):
27
    mocked_get.return_value = {
28
def test_interventionsets(mocked_request_json, app, connector):
29
    mocked_request_json.return_value = {
28 30
            "totalRecords": 2,
29 31
            "records": [
30 32
                {
......
48 50

  
49 51

  
50 52
@mock.patch('clicrdv.models.ClicRdv.get_json')
51
def test_interventionsets_details(mocked_get, app, connector):
52
    mocked_get.return_value = {
53
def test_interventionsets_details(mocked_request_json, app, connector):
54
    mocked_request_json.return_value = {
53 55
            "totalRecords": 2,
54 56
            "records": [
55 57
                {
......
78 80
    assert len(resp.json.get('data')) == 2
79 81
    assert resp.json.get('data')[0]['text'] == 'pour une personne'
80 82

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

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

  
115
    urlopen.return_value = StringIO('''{"availabletimeslots": [
120
    mock_response.json.return_value = {"availabletimeslots": [
116 121
            { "start": "2016-09-22 11:22:33" },
117 122
            { "start": "2016-09-21 12:34:56" }
118
        ]}''') # will be sorted
123
    ]} # will be sorted
124

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

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

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

  
151
@mock.patch('passerelle.utils.LoggedRequest.request')
152
def test_cancel_appointment(mocked_request, app, connector):
153
    obj_type = ContentType.objects.get_for_model(ClicRdv)
154
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API',
155
                                     key='apiuser')
156
    AccessRight.objects.create(codename='can_manage_appointment',
157
                resource_type=obj_type, resource_pk=connector.pk,
158
                apiuser=apiuser)
159
    mock_response = mock.Mock()
160
    mock_response.json.return_value = {'ok'}
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.LoggedRequest.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
    mock_response = mock.Mock()
175
    mock_response.json.return_value = {'ok'}
176
    resp = app.get('/clicrdv/test/63258/cancel?apikey=apiuser').json
177
    assert mocked_request.call_count == 1
178
    assert resp.get('err') == 1
179
    assert not resp['data']
141
-