Projet

Général

Profil

0002-base-resource-add-automatic-up-endpoint-21978.patch

Lauréline Guérin, 11 octobre 2019 15:08

Télécharger (6,2 ko)

Voir les différences:

Subject: [PATCH 2/3] base resource: add automatic 'up' endpoint (#21978)

if method 'check_status' is defined, provide a 'up' endpoint
 passerelle/base/models.py      | 13 ++++++
 passerelle/views.py            |  3 ++
 tests/test_generic_endpoint.py | 74 ++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+)
passerelle/base/models.py
19 19
from django.test import override_settings
20 20
from django.utils.text import slugify
21 21
from django.utils import timezone, six
22
from django.utils.six.moves.urllib.parse import urlparse
22 23
from django.utils.translation import ugettext_lazy as _
23 24
from django.utils.timezone import now
24 25
from django.core.files.base import ContentFile
......
34 35

  
35 36
import passerelle
36 37
import requests
38
from passerelle.utils.api import endpoint
39
from passerelle.utils.jsonresponse import APIError
37 40

  
38 41
KEYTYPE_CHOICES = (
39 42
    ('API', _('API Key')),
......
231 234
        for name, method in inspect.getmembers(self, predicate=inspect.ismethod):
232 235
            if hasattr(method, 'endpoint_info'):
233 236
                method.endpoint_info.object = self
237
                endpoint_name = method.endpoint_info.name
238
                if endpoint_name == 'up' and hasattr(self.check_status, 'not_implemented'):
239
                    # hide automatic up endpoint if check_status method is not implemented
240
                    continue
234 241
                for http_method in method.endpoint_info.methods:
235 242
                    # duplicate information to give each method its own entry
236 243
                    endpoint_info = copy.copy(method.endpoint_info)
......
260 267
        status = self.get_availability_status()
261 268
        return (status and status.down())
262 269

  
270
    @endpoint(description=_('Check service availability'))
271
    def up(self, request, **kwargs):
272
        if self.down():
273
            raise APIError('service not available')
274
        return {'err': 0}
275

  
263 276
    def export_json(self):
264 277
        d = {
265 278
            '@type': 'passerelle-resource',
passerelle/views.py
364 364
                self.endpoint = method
365 365
        if not self.endpoint:
366 366
            raise Http404()
367
        if kwargs.get('endpoint') == 'up' and hasattr(connector.check_status, 'not_implemented'):
368
            # hide automatic up endpoint if check_status method is not implemented
369
            raise Http404()
367 370
        return super(GenericEndpointView, self).dispatch(request, *args, **kwargs)
368 371

  
369 372
    def _allowed_methods(self):
tests/test_generic_endpoint.py
27 27

  
28 28
import utils
29 29

  
30
from django.core.urlresolvers import reverse
31

  
30 32
from passerelle.apps.arcgis.models import ArcGIS
31 33
from passerelle.base.models import ResourceLog, ProxyLogger, BaseResource, HTTPResource
34
from passerelle.base.models import ResourceStatus
32 35
from passerelle.apps.mdel.models import MDEL
33 36
from passerelle.contrib.stub_invoices.models import StubInvoicesConnector
34 37
from passerelle.utils.api import endpoint
......
403 406
    json_res = app.get('/stub-invoices/fake/httpcall?floating=', status=400).json
404 407
    assert json_res['err'] == 1
405 408
    assert json_res['err_desc'] == 'invalid value for parameter "floating"'
409

  
410

  
411
class DummyConnectorBase(BaseResource):
412
    def get_availability_status(self):
413
        # naive get_availability_status method for testing
414
        try:
415
            self.check_status()
416
        except Exception:
417
            return ResourceStatus(status='down')
418
        return ResourceStatus(status='up')
419

  
420
    class Meta:
421
        app_label = 'dummy'
422
        abstract = True
423

  
424

  
425
class DummyConnectorWithCheckStatus(DummyConnectorBase):
426
    def check_status(self):
427
        return
428

  
429

  
430
class DummyConnectorWithCheckStatusFailure(DummyConnectorBase):
431
    def check_status(self):
432
        raise Exception('dummy reason')
433

  
434

  
435
class DummyConnectorWithoutCheckStatus(DummyConnectorBase):
436
    pass
437

  
438

  
439
@pytest.mark.parametrize('connector_class, expected_status, expected_response', [
440
    (DummyConnectorWithCheckStatus, 200, {'err': 0}),
441
    (DummyConnectorWithCheckStatusFailure, 200,
442
     {'err_class': 'passerelle.utils.jsonresponse.APIError',
443
      'err_desc': 'service not available', 'data': None, 'err': 1}),
444
    (DummyConnectorWithoutCheckStatus, 404, None),
445
])
446
def test_generic_up_endpoint(db, app, connector_class, expected_status, expected_response):
447
    connector = connector_class()
448
    connector.id = 42
449

  
450
    url = reverse('generic-endpoint', kwargs={
451
        'connector': 'foo',
452
        'slug': 'foo',
453
        'endpoint': 'up',
454
    })
455
    patch_init = mock.patch('passerelle.views.GenericConnectorMixin.init_stuff')
456
    patch_object = mock.patch('passerelle.views.GenericEndpointView.get_object',
457
                              return_value=connector)
458
    with patch_init, patch_object:
459
        response = app.get(url, status=expected_status)
460
    if expected_response is not None:
461
        assert response.json == expected_response
462

  
463

  
464
@pytest.mark.parametrize('connector_class, expected', [
465
    (DummyConnectorWithCheckStatus, True),
466
    (DummyConnectorWithCheckStatusFailure, True),
467
    (DummyConnectorWithoutCheckStatus, False),
468
])
469
def test_generic_up_in_endpoints_infos(db, app, connector_class, expected):
470
    connector = connector_class()
471
    connector.id = 42
472

  
473
    up_endpoints = [
474
        ep for ep in connector.get_endpoints_infos()
475
        if ep.name == 'up']
476
    if expected:
477
        assert len(up_endpoints) == 1
478
    else:
479
        assert up_endpoints == []
406
-