From 96171e417c3370c3e628fcb783d549a21f45a9a2 Mon Sep 17 00:00:00 2001 From: Matthias Stevens Date: Fri, 11 Dec 2015 00:03:03 +0100 Subject: [PATCH 1/4] Added core API to request server info (geokey version + installed extensions), to be used by Sapelli. Test case included. Signed-off-by: Matthias Stevens --- geokey/core/tests/__init__.py | 0 geokey/core/tests/test_views.py | 34 +++++++++++++++++++++++++++ geokey/core/url/api.py | 7 ++++++ geokey/core/views.py | 41 +++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 geokey/core/tests/__init__.py create mode 100644 geokey/core/tests/test_views.py create mode 100644 geokey/core/views.py diff --git a/geokey/core/tests/__init__.py b/geokey/core/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/geokey/core/tests/test_views.py b/geokey/core/tests/test_views.py new file mode 100644 index 00000000..1e380184 --- /dev/null +++ b/geokey/core/tests/test_views.py @@ -0,0 +1,34 @@ +import json + +from django.test import TestCase +from django.core.urlresolvers import reverse, resolve +from django.http import HttpRequest + +from geokey.version import get_version + +from geokey.core.views import InfoAPIView + + +# ############################################################################ +# +# PUBLIC API VIEWS +# +# ############################################################################ + +class InfoAPIViewTest(TestCase): + def test_url(self): + self.assertEqual(reverse('api:info_api'), '/api/info/') + + resolved = resolve('/api/info/') + self.assertEqual(resolved.func.func_name, InfoAPIView.__name__) + + def test_get(self): + view = InfoAPIView.as_view() + request = HttpRequest() + request.method = 'GET' + response = view(request).render() + + self.assertEqual(response.status_code, 200) + + response_json = json.loads(response.content) + self.assertEqual(response_json.get('geokey_version'), get_version()) diff --git a/geokey/core/url/api.py b/geokey/core/url/api.py index 2e7d2861..769a4ef9 100644 --- a/geokey/core/url/api.py +++ b/geokey/core/url/api.py @@ -1,5 +1,7 @@ from django.conf.urls import patterns, url +from geokey.core.views import InfoAPIView + from geokey.projects import views as project_views from geokey.categories import views as category_views @@ -9,6 +11,11 @@ urlpatterns = patterns( '', + # ########################### + # CORE + # ########################### + url(r'^info/$', InfoAPIView.as_view(), name='info_api'), + # ########################### # USER # ########################### diff --git a/geokey/core/views.py b/geokey/core/views.py new file mode 100644 index 00000000..b07af997 --- /dev/null +++ b/geokey/core/views.py @@ -0,0 +1,41 @@ +from rest_framework.views import APIView +from rest_framework.response import Response + +from geokey.version import get_version + +from geokey.extensions.base import extensions + +# ############################################################################ +# +# PUBLIC API VIEWS +# +# ############################################################################ + +class InfoAPIView(APIView): + """ + API endpoint to get GeoKey server information + """ + def get(self, request): + """ + Returns GeoKey server information + + Parameter + --------- + request : rest_framework.request.Request + Represents the HTTP request + + Response + -------- + rest_framework.response.Response + Containing the GeoKey server information + """ + info = {} + # GeoKey version: + info['geokey_version'] = get_version() + # Installed extensions (only the ids): + info['installed_extensions'] = map(lambda ext: ext, extensions) + # TODO Add extension versions? + # TODO Add more info later? + + # Return info: + return Response(info) From 04671e517a4f4bc9fbdad308cfb8d434fef8c17a Mon Sep 17 00:00:00 2001 From: Matthias Stevens Date: Fri, 11 Dec 2015 13:07:09 +0100 Subject: [PATCH 2/4] Extensions can now optionally(!) register their version with GeoKey, the info API uses this information to report the versions of installed extensions. Because version is an optional argument of register() this should not break existing extension which do not (yet) pass their version to GeoKey. Signed-off-by: Matthias Stevens --- geokey/core/tests/test_views.py | 4 +++- geokey/core/views.py | 10 ++++++---- geokey/extensions/base.py | 5 ++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/geokey/core/tests/test_views.py b/geokey/core/tests/test_views.py index 1e380184..d29453e9 100644 --- a/geokey/core/tests/test_views.py +++ b/geokey/core/tests/test_views.py @@ -31,4 +31,6 @@ def test_get(self): self.assertEqual(response.status_code, 200) response_json = json.loads(response.content) - self.assertEqual(response_json.get('geokey_version'), get_version()) + self.assertIn('geokey', response_json) + geokey_json = response_json.get('geokey') + self.assertEqual(geokey_json.get('version'), get_version()) diff --git a/geokey/core/views.py b/geokey/core/views.py index b07af997..269d111d 100644 --- a/geokey/core/views.py +++ b/geokey/core/views.py @@ -30,11 +30,13 @@ def get(self, request): Containing the GeoKey server information """ info = {} + gk_info = info['geokey'] = {} # GeoKey version: - info['geokey_version'] = get_version() - # Installed extensions (only the ids): - info['installed_extensions'] = map(lambda ext: ext, extensions) - # TODO Add extension versions? + gk_info['version'] = get_version() + # Installed extensions (with their version): + gk_info['installed_extensions'] = map( + lambda (ext_id, ext): {ext_id: {'version': ext['version']}}, + extensions.iteritems()) # TODO Add more info later? # Return info: diff --git a/geokey/extensions/base.py b/geokey/extensions/base.py index 31e8738d..afe874ff 100644 --- a/geokey/extensions/base.py +++ b/geokey/extensions/base.py @@ -9,7 +9,7 @@ class ExtensionExists(BaseException): pass -def register(ext_id, name, display_admin=False, superuser=False): +def register(ext_id, name, display_admin=False, superuser=False, version=''): """ Registeres a new extension to the system. @@ -23,6 +23,8 @@ def register(ext_id, name, display_admin=False, superuser=False): Indicates if the extension provides pages for the admin interface superuser : Boolean Indicates if the extentsion is available for superusers only + version : str + Version of the extension (optional) Raises ------ @@ -37,6 +39,7 @@ def register(ext_id, name, display_admin=False, superuser=False): extensions[ext_id] = { 'ext_id': ext_id, 'name': name, + 'version': version, 'display_admin': display_admin, 'superuser': superuser, 'index_url': ext_id + ':index' From a58164c1f4dc1037bfeeec95643a8953b994b049 Mon Sep 17 00:00:00 2001 From: Matthias Stevens Date: Fri, 11 Dec 2015 14:55:11 +0100 Subject: [PATCH 3/4] Improvements to info API: - check if 'version' field exists on extension dict - hide superuser extensions unless request comes from superuser - improved test case Also added a deregister() method for extensions (only for testing purposes) Signed-off-by: Matthias Stevens --- geokey/core/tests/test_views.py | 78 +++++++++++++++++++++++++++++++-- geokey/core/views.py | 10 ++++- geokey/extensions/base.py | 15 ++++++- 3 files changed, 96 insertions(+), 7 deletions(-) diff --git a/geokey/core/tests/test_views.py b/geokey/core/tests/test_views.py index d29453e9..12a9d8c5 100644 --- a/geokey/core/tests/test_views.py +++ b/geokey/core/tests/test_views.py @@ -3,10 +3,12 @@ from django.test import TestCase from django.core.urlresolvers import reverse, resolve from django.http import HttpRequest +from django.contrib.auth.models import AnonymousUser from geokey.version import get_version - +from geokey.users.models import User from geokey.core.views import InfoAPIView +from geokey.extensions.base import register, deregister # ############################################################################ @@ -16,21 +18,91 @@ # ############################################################################ class InfoAPIViewTest(TestCase): + def setUp(self): + # register bogus extensions: + register('A', 'A', display_admin=True, superuser=False, version='1.0') + register('B', 'B', display_admin=True, superuser=False) + register('S', 'S', display_admin=True, superuser=True, version='0.1.0') + + def tearDown(self): + # deregister bogus extensions: + deregister('A') + deregister('B') + deregister('S') + def test_url(self): self.assertEqual(reverse('api:info_api'), '/api/info/') - resolved = resolve('/api/info/') self.assertEqual(resolved.func.func_name, InfoAPIView.__name__) - def test_get(self): + def contains_extension(self, ext_id, exts_json): + for ext in exts_json: + if ext_id in ext: + return True + return False + + def test_get_with_anonymous(self): view = InfoAPIView.as_view() request = HttpRequest() request.method = 'GET' + request.user = AnonymousUser() response = view(request).render() + + self.assertEqual(response.status_code, 200) + + response_json = json.loads(response.content) + self.assertIn('geokey', response_json) + geokey_json = response_json.get('geokey') + self.assertEqual(geokey_json.get('version'), get_version()) + self.assertIn('installed_extensions', geokey_json) + + exts_json = response_json.get('geokey').get('installed_extensions') + self.assertTrue(self.contains_extension('A', exts_json)) + self.assertTrue(self.contains_extension('B', exts_json)) + # superuser extension must be hidden: + self.assertFalse(self.contains_extension('S', exts_json)) + + def test_get_with_user(self): + view = InfoAPIView.as_view() + request = HttpRequest() + request.method = 'GET' + request.user = User.objects.create_user("a@a.com", "test", "test") + response = view(request).render() + self.assertEqual(response.status_code, 200) response_json = json.loads(response.content) + self.assertIn('geokey', response_json) geokey_json = response_json.get('geokey') self.assertEqual(geokey_json.get('version'), get_version()) + self.assertIn('installed_extensions', geokey_json) + + exts_json = response_json.get('geokey').get('installed_extensions') + self.assertTrue(self.contains_extension('A', exts_json)) + self.assertTrue(self.contains_extension('B', exts_json)) + # superuser extension must be hidden: + self.assertFalse(self.contains_extension('S', exts_json)) + + def test_get_with_superuser(self): + view = InfoAPIView.as_view() + request = HttpRequest() + request.method = 'GET' + request.user = User.objects.create_superuser("s@s.com", "test", "test") + response = view(request).render() + + self.assertEqual(response.status_code, 200) + + response_json = json.loads(response.content) + + self.assertIn('geokey', response_json) + geokey_json = response_json.get('geokey') + self.assertEqual(geokey_json.get('version'), get_version()) + self.assertIn('installed_extensions', geokey_json) + + exts_json = response_json.get('geokey').get('installed_extensions') + self.assertTrue(self.contains_extension('A', exts_json)) + self.assertTrue(self.contains_extension('B', exts_json)) + # superuser extension must be shown: + self.assertTrue(self.contains_extension('S', exts_json)) diff --git a/geokey/core/views.py b/geokey/core/views.py index 269d111d..88ffcd1f 100644 --- a/geokey/core/views.py +++ b/geokey/core/views.py @@ -35,8 +35,14 @@ def get(self, request): gk_info['version'] = get_version() # Installed extensions (with their version): gk_info['installed_extensions'] = map( - lambda (ext_id, ext): {ext_id: {'version': ext['version']}}, - extensions.iteritems()) + lambda (ext_id, ext): + {ext_id: + {'version': + (ext['version'] if 'version' in ext else None)}}, + filter( + lambda (ext_id, ext): + request.user.is_superuser or not ext['superuser'], + extensions.iteritems())) # TODO Add more info later? # Return info: diff --git a/geokey/extensions/base.py b/geokey/extensions/base.py index afe874ff..dc371abd 100644 --- a/geokey/extensions/base.py +++ b/geokey/extensions/base.py @@ -9,9 +9,9 @@ class ExtensionExists(BaseException): pass -def register(ext_id, name, display_admin=False, superuser=False, version=''): +def register(ext_id, name, display_admin=False, superuser=False, version=None): """ - Registeres a new extension to the system. + Registers a new extension to the system. Parameters ---------- @@ -44,3 +44,14 @@ def register(ext_id, name, display_admin=False, superuser=False, version=''): 'superuser': superuser, 'index_url': ext_id + ':index' } + +def deregister(ext_id): + """ + De-registers an extension to the system. Only to be used for testing. + + Parameters + ---------- + ext_id : str + Unique identifier for the extension + """ + extensions.pop(ext_id, None) From d0f56a523f0a20f53377fb3de415e6d7a6f86ba7 Mon Sep 17 00:00:00 2001 From: Matthias Stevens Date: Fri, 11 Dec 2015 15:35:15 +0100 Subject: [PATCH 4/4] Fixed extensions tests (test_base.py) to reflect new (& much older!) changes to extension registration logic. Also added proper setUp() and tearDown(). This should resolve the travis failures on this branch... Signed-off-by: Matthias Stevens --- geokey/extensions/tests/test_base.py | 40 +++++++++++----------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/geokey/extensions/tests/test_base.py b/geokey/extensions/tests/test_base.py index 582f8cb1..b34d1ed5 100644 --- a/geokey/extensions/tests/test_base.py +++ b/geokey/extensions/tests/test_base.py @@ -1,33 +1,25 @@ from django.test import TestCase -from geokey.extensions.base import ExtensionExists, register, extensions as ext +from ..base import ExtensionExists, register, deregister, extensions as ext class RegisterExtensionTest(TestCase): + def setUp(self): + register('test_ext', 'Test', True, True, '1.0.0') + + def tearDown(self): + deregister('test_ext') + def test(self): - register('text_ext', 'Test', True) - extension = ext.get('text_ext') - self.assertEqual(extension.get('ext_id'), 'text_ext') + extension = ext.get('test_ext') + self.assertEqual(extension.get('ext_id'), 'test_ext') self.assertEqual(extension.get('name'), 'Test') - self.assertEqual(extension.get('index_url'), 'text_ext:index') + self.assertEqual(extension.get('index_url'), 'test_ext:index') self.assertTrue(extension.get('display_admin')) + self.assertTrue(extension.get('superuser')) + self.assertEqual(extension.get('version'), '1.0.0') def test_exiting(self): - ext['text_ext'] = { - 'ext_id': 'text_ext', - 'name': 'Test', - 'display_admin': True, - 'index_url': 'text_ext:index' - } - try: - register('text_ext', 'Test Name', False) - except ExtensionExists: - extension = ext.get('text_ext') - self.assertEqual(extension.get('ext_id'), 'text_ext') - self.assertEqual(extension.get('name'), 'Test') - self.assertEqual( - extension.get('index_url'), - 'text_ext:index' - ) - self.assertTrue(extension.get('display_admin')) - else: - self.fail('ExtensionExists not raised') + self.assertRaises( + ExtensionExists, + register, + 'test_ext', 'abc', False, False, '0.0.1')