0001-clicrdv-replace-urllib2-by-connector-s-requests-7898.patch
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 |
- |