0007-toulouse_smart-add-update-intervention-endpoint-5523.patch
passerelle/contrib/toulouse_smart/models.py | ||
---|---|---|
12 | 12 |
# GNU Affero General Public License for more details. |
13 | 13 |
# |
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
import datetime |
18 | 18 | |
19 | 19 |
import lxml.etree as ET |
20 |
from django.conf import settings |
|
20 | 21 |
from django.contrib.postgres.fields import JSONField |
21 | 22 |
from django.db import models |
22 | 23 |
from django.urls import reverse |
24 |
from django.utils.six.moves.urllib import parse as urlparse |
|
23 | 25 |
from django.utils.text import slugify |
24 | 26 |
from django.utils.timezone import now |
25 | 27 |
from django.utils.translation import ugettext_lazy as _ |
26 | 28 |
from requests import RequestException |
27 | 29 | |
28 | 30 |
from passerelle.base.models import BaseResource, HTTPResource |
31 |
from passerelle.base.signature import sign_query |
|
29 | 32 |
from passerelle.utils import xml |
30 | 33 |
from passerelle.utils.api import endpoint |
31 | 34 |
from passerelle.utils.jsonresponse import APIError |
32 | 35 | |
33 | 36 |
from . import schemas, utils |
34 | 37 | |
35 | 38 | |
36 | 39 |
class ToulouseSmartResource(BaseResource, HTTPResource): |
... | ... | |
187 | 190 |
response = self.request(url, json=post_data) |
188 | 191 |
try: |
189 | 192 |
result = response.json() |
190 | 193 |
except ValueError: |
191 | 194 |
raise APIError('invalid json, got: %s' % response.text) |
192 | 195 |
self.set('intervention-%s' % result['id'], form_url) |
193 | 196 |
return {'data': result} |
194 | 197 | |
198 |
@endpoint( |
|
199 |
name='update-intervention', |
|
200 |
methods=['post'], |
|
201 |
description=_('Update an intervention status'), |
|
202 |
perm='can_access', |
|
203 |
parameters={ |
|
204 |
'id': {'description': _('Intervention identifier')}, |
|
205 |
'trigger': {'description': _('W.C.S trigger')}, |
|
206 |
}, |
|
207 |
) |
|
208 |
def update_intervention(self, request, id, trigger): |
|
209 |
try: |
|
210 |
form_url = self.get('intervention-%s' % id) |
|
211 |
except KeyError: |
|
212 |
raise APIError("Cannot find intervention '%s'" % id) |
|
213 | ||
214 |
base_url = '%sjump/trigger/%s' % (form_url, trigger) |
|
215 |
scheme, netloc, path, params, query, fragment = urlparse.urlparse(base_url) |
|
216 |
services = settings.KNOWN_SERVICES.get('wcs', {}) |
|
217 |
service = None |
|
218 |
for service in services.values(): |
|
219 |
remote_url = service.get('url') |
|
220 |
r_scheme, r_netloc, r_path, r_params, r_query, r_fragment = urlparse.urlparse(remote_url) |
|
221 |
if r_scheme == scheme and r_netloc == netloc: |
|
222 |
break |
|
223 |
else: |
|
224 |
raise APIError('Cannot find wcs service for %s' % base_url) |
|
225 | ||
226 |
headers = { |
|
227 |
'Content-Type': 'application/json', |
|
228 |
'Accept': 'application/json', |
|
229 |
} |
|
230 |
query_string = 'orig=%s' % service.get('orig') |
|
231 |
parameters = sign_query(query_string, key=service.get('secret')) |
|
232 |
url = '%s?%s' % (base_url, parameters) |
|
233 |
response = self.requests.post(url, headers=headers) |
|
234 |
try: |
|
235 |
result = response.json() |
|
236 |
except ValueError: |
|
237 |
raise APIError('failed to post to %s (%s): %s' % (url, response.status_code, response.text)) |
|
238 |
return {'err': result.get('err', 1), 'data': result} |
|
239 | ||
195 | 240 | |
196 | 241 |
class Cache(models.Model): |
197 | 242 |
resource = models.ForeignKey( |
198 | 243 |
verbose_name=_('Resource'), |
199 | 244 |
to=ToulouseSmartResource, |
200 | 245 |
on_delete=models.CASCADE, |
201 | 246 |
related_name='cache_entries', |
202 | 247 |
) |
tests/test_toulouse_smart.py | ||
---|---|---|
40 | 40 |
slug='test', |
41 | 41 |
description='Test', |
42 | 42 |
webservice_base_url='https://smart.example.com/', |
43 | 43 |
basic_auth_username='username', |
44 | 44 |
basic_auth_password='password', |
45 | 45 |
) |
46 | 46 | |
47 | 47 | |
48 |
@pytest.fixture |
|
49 |
def wcs_service(settings): |
|
50 |
wcs_service = { |
|
51 |
'default': { |
|
52 |
'title': 'test', |
|
53 |
'url': 'https://wcs.example.com', |
|
54 |
'secret': 'xxx', |
|
55 |
'orig': 'passerelle', |
|
56 |
}, |
|
57 |
} |
|
58 |
settings.KNOWN_SERVICES = {'wcs': wcs_service} |
|
59 |
return wcs_service |
|
60 | ||
61 | ||
48 | 62 |
def mock_response(*path_contents): |
49 | 63 |
def decorator(func): |
50 | 64 |
@httmock.urlmatch() |
51 | 65 |
def error(url, request): |
52 | 66 |
assert False, 'request to %s' % url.geturl() |
53 | 67 | |
54 | 68 |
def register(path, payload, content, status_code=200): |
55 | 69 |
@httmock.urlmatch(path=path) |
... | ... | |
396 | 410 |
@mock_response( |
397 | 411 |
['/v1/type-intervention', None, INTERVENTION_TYPES], |
398 | 412 |
['/v1/intervention', CREATE_INTERVENTION_QUERY, 'not json content'], |
399 | 413 |
) |
400 | 414 |
def test_create_intervention_error_content(app, smart): |
401 | 415 |
resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD) |
402 | 416 |
assert resp.json['err'] |
403 | 417 |
assert 'invalid json' in resp.json['err_desc'] |
418 | ||
419 | ||
420 |
WCS_RESPONSE_SUCCESS = '{"err": 0, "url": null}' |
|
421 |
WCS_RESPONSE_ERROR = '{"err": 1, "err_class": "Page non trouv\u00e9e", "err_desc": null}' |
|
422 | ||
423 | ||
424 |
@mock_response( |
|
425 |
['/v1/type-intervention', None, INTERVENTION_TYPES], |
|
426 |
['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')], |
|
427 |
['/foo/2/jump/trigger/created', None, WCS_RESPONSE_SUCCESS], |
|
428 |
) |
|
429 |
def test_update_intervention(app, smart, wcs_service): |
|
430 |
resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD) |
|
431 |
assert not resp.json['err'] |
|
432 |
intervention_id = resp.json['data']['id'] |
|
433 |
resp = app.post_json(URL + 'update-intervention?id=%s&trigger=created' % intervention_id) |
|
434 |
assert not resp.json['err'] |
|
435 |
assert resp.json['data'] == json.loads(WCS_RESPONSE_SUCCESS) |
|
436 | ||
437 | ||
438 |
def test_update_intervention_wrong_id(app, smart): |
|
439 |
intervention_id = '96bc8712-21a6-4fba-a511-740d6f1bd0bc' |
|
440 |
resp = app.post_json(URL + 'update-intervention?id=%s&trigger=created' % intervention_id) |
|
441 |
assert resp.json['err'] |
|
442 |
assert 'Cannot find intervention' in resp.json['err_desc'] |
|
443 | ||
444 | ||
445 |
@mock_response( |
|
446 |
['/v1/type-intervention', None, INTERVENTION_TYPES], |
|
447 |
['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')], |
|
448 |
) |
|
449 |
def test_update_intervention_wrong_service(app, smart, wcs_service): |
|
450 |
wcs_service['default']['url'] = 'http://wcs.example.com/smart_form/' |
|
451 |
resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD) |
|
452 |
assert not resp.json['err'] |
|
453 |
intervention_id = resp.json['data']['id'] |
|
454 |
resp = app.post_json(URL + 'update-intervention?id=%s&trigger=created' % intervention_id) |
|
455 |
assert resp.json['err'] |
|
456 |
assert 'Cannot find wcs service' in resp.json['err_desc'] |
|
457 | ||
458 | ||
459 |
@mock_response( |
|
460 |
['/v1/type-intervention', None, INTERVENTION_TYPES], |
|
461 |
['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')], |
|
462 |
['/foo/2/jump/trigger/created', None, None, 500], |
|
463 |
) |
|
464 |
def test_update_intervention_error_wcs(app, smart, wcs_service): |
|
465 |
resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD) |
|
466 |
assert not resp.json['err'] |
|
467 |
intervention_id = resp.json['data']['id'] |
|
468 |
resp = app.post_json(URL + 'update-intervention?id=%s&trigger=created' % intervention_id) |
|
469 |
assert resp.json['err'] |
|
470 |
assert 'failed to post' in resp.json['err_desc'] |
|
471 | ||
472 | ||
473 |
@mock_response( |
|
474 |
['/v1/type-intervention', None, INTERVENTION_TYPES], |
|
475 |
['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')], |
|
476 |
['/foo/2/jump/trigger/created', None, WCS_RESPONSE_ERROR], |
|
477 |
) |
|
478 |
def test_update_intervention_wrong_trigger(app, smart, wcs_service): |
|
479 |
resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD) |
|
480 |
assert not resp.json['err'] |
|
481 |
intervention_id = resp.json['data']['id'] |
|
482 |
resp = app.post_json(URL + 'update-intervention?id=%s&trigger=created' % intervention_id) |
|
483 |
assert resp.json['err'] |
|
484 |
assert resp.json['data'] == json.loads(WCS_RESPONSE_ERROR) |
|
404 |
- |