Added /processingnodes/options/ API, unit tests

pull/111/head
Piero Toffanin 2017-03-01 13:53:16 -05:00
rodzic 2461e0701c
commit e14fc574a6
4 zmienionych plików z 92 dodań i 6 usunięć

Wyświetl plik

@ -1,6 +1,10 @@
import django_filters
from django.core.exceptions import ObjectDoesNotExist
from django_filters.rest_framework import FilterSet
from rest_framework import serializers, viewsets
from guardian.shortcuts import get_objects_for_user
from rest_framework import serializers, viewsets, exceptions
from rest_framework.response import Response
from rest_framework.views import APIView
from nodeodm.models import ProcessingNode
@ -35,4 +39,47 @@ class ProcessingNodeViewSet(viewsets.ModelViewSet):
pagination_class = None
serializer_class = ProcessingNodeSerializer
queryset = ProcessingNode.objects.all()
queryset = ProcessingNode.objects.all()
class ProcessingNodeOptionsView(APIView):
"""
Display the intersection of all ProcessingNode's available_options fields.
Each ProcessingNode has its own set of available_options. When a user relies on the
automatic node selection feature, it's better to have a list of available_options that
is common among all ProcessingNode.
"""
queryset = ProcessingNode.objects.all()
def get(self, request):
nodes = get_objects_for_user(request.user, 'view_processingnode', ProcessingNode, accept_global_perms=False)
common_options = []
for node in nodes:
# Skip offline nodes
if not node.is_online():
continue
# First? Just populate
if len(common_options) == 0 and len(node.available_options) > 0:
common_options = node.available_options
else:
# Remove all options that are in common_options,
# but that are not in node.available_options
for common_option in common_options:
found = False
for option in node.available_options:
if common_option['name'] == option['name']:
found = True
break
# Mark for deletion
if not found:
common_option['_delete'] = True
common_options = [co for co in common_options if not '_delete' in co]
return Response(common_options)

Wyświetl plik

@ -1,7 +1,7 @@
from django.conf.urls import url, include
from .projects import ProjectViewSet
from .tasks import TaskViewSet, TaskTiles, TaskTilesJson, TaskDownloads, TaskAssets
from .processingnodes import ProcessingNodeViewSet
from .processingnodes import ProcessingNodeViewSet, ProcessingNodeOptionsView
from rest_framework_nested import routers
from rest_framework_jwt.views import obtain_jwt_token
@ -13,6 +13,8 @@ tasks_router = routers.NestedSimpleRouter(router, r'projects', lookup='project')
tasks_router.register(r'tasks', TaskViewSet, base_name='projects-tasks')
urlpatterns = [
url(r'processingnodes/options/$', ProcessingNodeOptionsView.as_view()),
url(r'^', include(router.urls)),
url(r'^', include(tasks_router.urls)),

Wyświetl plik

@ -1,14 +1,15 @@
import datetime
from django.contrib.auth.models import User
from guardian.shortcuts import assign_perm
from guardian.shortcuts import assign_perm, get_objects_for_user
from django.utils import timezone
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework_jwt.settings import api_settings
from app import pending_actions
from app.models import Project, Task
from nodeodm.models import ProcessingNode
from nodeodm.models import ProcessingNode, OFFLINE_MINUTES
from .classes import BootTestCase
@ -237,6 +238,10 @@ class TestApi(BootTestCase):
res = client.get('/api/processingnodes/{}/'.format(pnode.id))
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
# Cannot get options as guest
res = client.get('/api/processingnodes/options/')
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
client.login(username="testuser", password="test1234")
# Cannot list processing nodes, unless permissions have been granted
@ -307,6 +312,39 @@ class TestApi(BootTestCase):
self.assertTrue(len(res.data) == 2)
self.assertTrue(res.data[1]["port"] == 1000)
# Test available_options intersection
# (with normal user)
client.login(username="testuser", password="test1234")
user = User.objects.get(username="testuser")
self.assertFalse(user.is_superuser)
p1 = ProcessingNode.objects.create(hostname="invalid-host", port=11223,
last_refreshed=timezone.now(),
available_options=[{'name': 'a'}, {'name': 'b'}])
p2 = ProcessingNode.objects.create(hostname="invalid-host-2", port=11223,
last_refreshed=timezone.now(),
available_options=[{'name': 'a'}, {'name': 'c'}])
p3 = ProcessingNode.objects.create(hostname="invalid-host-3", port=11223,
last_refreshed=timezone.now(),
available_options=[{'name': 'd'}])
p4 = ProcessingNode.objects.create(hostname="invalid-host-4", port=11223,
last_refreshed=timezone.now() - datetime.timedelta(minutes=OFFLINE_MINUTES * 2),
available_options=[{'name': 'd'}]) # offline
assign_perm('view_processingnode', user, p1)
assign_perm('view_processingnode', user, p2)
assign_perm('view_processingnode', user, p4)
self.assertFalse(user.has_perm('view_processingnode', p3))
nodes_available = get_objects_for_user(user, 'view_processingnode', ProcessingNode, accept_global_perms=False).exclude(available_options=dict())
self.assertTrue(len(nodes_available) == 3)
res = client.get('/api/processingnodes/options/')
self.assertEqual(res.status_code, status.HTTP_200_OK)
self.assertTrue(len(res.data) == 1)
self.assertTrue(res.data[0]['name'] == 'a')
def test_token_auth(self):
client = APIClient()

Wyświetl plik

@ -14,7 +14,6 @@ from django.db.models import signals
from datetime import datetime, timedelta
from .exceptions import ProcessingError, ProcessingTimeout
import simplejson
import django.utils.timezone
def api(func):