Projet

Général

Profil

0007-toulouse_smart-add-update-intervention-endpoint-5523.patch

Nicolas Roche, 09 juillet 2021 22:00

Télécharger (8,4 ko)

Voir les différences:

Subject: [PATCH 7/7] toulouse_smart: add update-intervention endpoint (#55230)

 passerelle/contrib/toulouse_smart/models.py | 45 ++++++++++++
 tests/test_toulouse_smart.py                | 81 +++++++++++++++++++++
 2 files changed, 126 insertions(+)
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
-