0002-base-resource-add-automatic-up-endpoint-21978.patch
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 |
- |