forked from ubccr/coldfront
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #242 from fasrc/cp_api2
Add API plugin
- Loading branch information
Showing
10 changed files
with
256 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,7 @@ | |
'django_tables2', | ||
'table', | ||
'rest_framework_datatables', | ||
'rest_framework', | ||
'easy_pdf', | ||
] | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from coldfront.config.base import INSTALLED_APPS | ||
|
||
INSTALLED_APPS += [ | ||
'coldfront.plugins.api', | ||
] | ||
|
||
REST_FRAMEWORK = { | ||
'DEFAULT_PERMISSION_CLASSES': [ | ||
'rest_framework.permissions.IsAuthenticated', | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from rest_framework import serializers | ||
|
||
from django.contrib.auth import get_user_model | ||
|
||
from coldfront.core.resource.models import Resource | ||
from coldfront.core.project.models import Project, ProjectUser | ||
from coldfront.core.allocation.models import Allocation, AllocationUser | ||
|
||
|
||
class UserSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = get_user_model() | ||
fields = ('id', 'full_name') | ||
|
||
|
||
class ResourceSerializer(serializers.ModelSerializer): | ||
resource_type = serializers.SlugRelatedField(slug_field='name', read_only=True) | ||
|
||
class Meta: | ||
model = Resource | ||
fields = ('id', 'resource_type', 'name', 'description', 'is_allocatable') | ||
|
||
|
||
class AllocationPctUsageField(serializers.Field): | ||
def to_representation(self, data): | ||
if data.usage and float(data.size): | ||
return round((data.usage / float(data.size) * 100), 2) | ||
if data.usage == 0: | ||
return 0 | ||
return None | ||
|
||
|
||
class AllocationSerializer(serializers.ModelSerializer): | ||
resource = serializers.ReadOnlyField(source='get_resources_as_string') | ||
project = serializers.SlugRelatedField(slug_field='title', read_only=True) | ||
status = serializers.SlugRelatedField(slug_field='name', read_only=True) | ||
size = serializers.FloatField() | ||
pct_full = AllocationPctUsageField(source='*') | ||
|
||
class Meta: | ||
model = Allocation | ||
fields = ( | ||
'id', | ||
'project', | ||
'resource', | ||
'status', | ||
'path', | ||
'size', | ||
'usage', | ||
'pct_full', | ||
'cost', | ||
) | ||
|
||
|
||
class ProjAllocationSerializer(serializers.ModelSerializer): | ||
resource = serializers.ReadOnlyField(source='get_resources_as_string') | ||
status = serializers.SlugRelatedField(slug_field='name', read_only=True) | ||
size = serializers.FloatField() | ||
|
||
class Meta: | ||
model = Allocation | ||
fields = ('id', 'resource', 'status', 'path', 'size', 'usage') | ||
|
||
|
||
class ProjectUserSerializer(serializers.ModelSerializer): | ||
user = serializers.SlugRelatedField(slug_field='full_name', read_only=True) | ||
status = serializers.SlugRelatedField(slug_field='name', read_only=True) | ||
role = serializers.SlugRelatedField(slug_field='name', read_only=True) | ||
|
||
class Meta: | ||
model = ProjectUser | ||
fields = ('user', 'role', 'status') | ||
|
||
|
||
class ProjectSerializer(serializers.ModelSerializer): | ||
pi = serializers.SlugRelatedField(slug_field='full_name', read_only=True) | ||
status = serializers.SlugRelatedField(slug_field='name', read_only=True) | ||
users = ProjectUserSerializer(source='projectuser_set', many=True, read_only=True) | ||
allocations = ProjAllocationSerializer(source='allocation_set', many=True, read_only=True) | ||
|
||
class Meta: | ||
model = Project | ||
fields = ('id', 'title', 'pi', 'status', 'users', 'allocations') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from rest_framework import status | ||
from rest_framework.test import APITestCase, APIRequestFactory | ||
from coldfront.core.test_helpers.factories import setup_models, AllocationFactory | ||
from coldfront.core.allocation.models import Allocation | ||
from coldfront.core.project.models import Project | ||
|
||
|
||
class ColdfrontAPI(APITestCase): | ||
"""Tests for the Coldfront rest API""" | ||
|
||
fixtures = [ | ||
"coldfront/core/test_helpers/test_data/test_fixtures/ifx.json", | ||
] | ||
|
||
@classmethod | ||
def setUpTestData(cls): | ||
"""Create some test data""" | ||
setup_models(cls) | ||
cls.additional_allocations = [ | ||
AllocationFactory() for i in list(range(50)) | ||
] | ||
|
||
def test_requires_login(self): | ||
"""Test that the API requires authentication""" | ||
response = self.client.get('/api/') | ||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) | ||
|
||
def test_allocation_api_permissions(self): | ||
"""Test that accessing the allocation API view as an admin returns all | ||
allocations, and that accessing it as a user returns only the allocations | ||
for that user""" | ||
# login as admin | ||
self.client.force_login(self.admin_user) | ||
response = self.client.get('/api/allocations/', format='json') | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
self.assertEqual(len(response.data), Allocation.objects.all().count()) | ||
|
||
self.client.force_login(self.pi_user) | ||
response = self.client.get('/api/allocations/', format='json') | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
self.assertEqual(len(response.data), 1) | ||
|
||
def test_project_api_permissions(self): | ||
"""Confirm permissions for project API: | ||
admin user should be able to access everything | ||
Projectusers should be able to access only their projects | ||
""" | ||
# login as admin | ||
self.client.force_login(self.admin_user) | ||
response = self.client.get('/api/projects/', format='json') | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
self.assertEqual(len(response.data), Allocation.objects.all().count()) | ||
|
||
self.client.force_login(self.pi_user) | ||
response = self.client.get('/api/projects/', format='json') | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
self.assertEqual(len(response.data), 1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.urls import include, path | ||
from rest_framework import routers | ||
from coldfront.plugins.api import views | ||
|
||
router = routers.DefaultRouter() | ||
router.register(r'allocations', views.AllocationViewSet, basename='allocations') | ||
router.register(r'resources', views.ResourceViewSet, basename='resources') | ||
router.register(r'projects', views.ProjectViewSet, basename='projects') | ||
|
||
urlpatterns = [ | ||
path('', include(router.urls)), | ||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
from rest_framework import viewsets | ||
from django.db.models import Q | ||
|
||
from coldfront.core.allocation.models import Allocation | ||
from coldfront.core.resource.models import Resource | ||
from coldfront.core.project.models import Project | ||
from coldfront.plugins.api import serializers | ||
|
||
|
||
class ResourceViewSet(viewsets.ReadOnlyModelViewSet): | ||
serializer_class = serializers.ResourceSerializer | ||
queryset = Resource.objects.all() | ||
|
||
|
||
class AllocationViewSet(viewsets.ReadOnlyModelViewSet): | ||
serializer_class = serializers.AllocationSerializer | ||
# permission_classes = (permissions.IsAuthenticatedOrReadOnly,) | ||
|
||
def get_queryset(self): | ||
allocations = Allocation.objects.prefetch_related( | ||
'project', 'project__pi', 'status' | ||
) | ||
|
||
if self.request.user.is_superuser or self.request.user.has_perm( | ||
'allocation.can_view_all_allocations' | ||
): | ||
allocations = allocations.order_by('project') | ||
else: | ||
allocations = allocations.filter( | ||
Q(project__status__name__in=['New', 'Active']) & | ||
( | ||
( | ||
Q(project__projectuser__role__name='Manager') | ||
& Q(project__projectuser__user=self.request.user) | ||
) | ||
| Q(project__pi=self.request.user) | ||
) | ||
).distinct().order_by('project') | ||
|
||
return allocations | ||
|
||
|
||
class ProjectViewSet(viewsets.ReadOnlyModelViewSet): | ||
serializer_class = serializers.ProjectSerializer | ||
|
||
|
||
def get_queryset(self): | ||
projects = Project.objects.prefetch_related('status') | ||
|
||
if self.request.user.is_superuser or self.request.user.has_perm( | ||
'project.can_view_all_projects' | ||
): | ||
projects = projects.order_by('pi') | ||
else: | ||
projects = projects.filter( | ||
Q(status__name__in=['New', 'Active']) & | ||
( | ||
( | ||
Q(projectuser__role__name='Manager') | ||
& Q(projectuser__user=self.request.user) | ||
) | ||
| Q(pi=self.request.user) | ||
) | ||
).distinct().order_by('pi') | ||
|
||
return projects |