[PATCH v4 04/10] REST: Add Persons to the API
Finucane, Stephen
stephen.finucane at intel.com
Wed May 25 19:22:29 AEST 2016
On 20 May 14:17, Andy Doan wrote:
> This exports person objects via the REST API.
>
> Security Constraints:
> * The API is read-only to authenticated users
>
> Signed-off-by: Andy Doan <andy.doan at linaro.org>
> ---
> patchwork/rest_serializers.py | 13 +++++++++-
> patchwork/tests/test_rest_api.py | 54 ++++++++++++++++++++++++++++++++++++++++
> patchwork/views/rest_api.py | 30 ++++++++++++++++++----
> 3 files changed, 91 insertions(+), 6 deletions(-)
>
> diff --git a/patchwork/rest_serializers.py b/patchwork/rest_serializers.py
> index bbeae37..0bb8f23 100644
> --- a/patchwork/rest_serializers.py
> +++ b/patchwork/rest_serializers.py
> @@ -19,7 +19,18 @@
>
> from rest_framework.serializers import HyperlinkedModelSerializer
>
> -from patchwork.models import Project
> +from patchwork.models import Person, Project
> +
> +
> +class PersonSerializer(HyperlinkedModelSerializer):
> + class Meta:
> + model = Person
> + exclude = ('user',)
You've added a 'users' endpoint in this revision. This being the case,
it now makes sense to expose the user object, or at least a URL to the
endpoint (it didn't before as 'user' was a simple integer that we
couldn't do anything with). You could also drop the 'username', if you
wanted to, though there may still be value in keeping it?
> +
> + def to_representation(self, instance):
> + data = super(PersonSerializer, self).to_representation(instance)
> + data['username'] = instance.user.username if instance.user else ''
> + return data
>
>
> class ProjectSerializer(HyperlinkedModelSerializer):
> diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py
> index 56c07a4..05886bd 100644
> --- a/patchwork/tests/test_rest_api.py
> +++ b/patchwork/tests/test_rest_api.py
> @@ -118,3 +118,57 @@ class TestProjectAPI(APITestCase):
> resp = self.client.delete(self.api_url(1))
> self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code)
> self.assertEqual(1, Project.objects.all().count())
> +
> +
> + at unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
> +class TestPersonAPI(APITestCase):
> + fixtures = ['default_states']
> +
> + @staticmethod
> + def api_url(item=None):
> + if item is None:
> + return reverse('api_1.0:person-list')
> + return reverse('api_1.0:person-detail', args=[item])
> +
> + def test_anonymous_list(self):
> + """The API should reject anonymous users."""
> + resp = self.client.get(self.api_url())
> + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code)
> +
> + def test_authenticated_list(self):
> + """This API requires authenticated users."""
> + user = create_user()
> + self.client.force_authenticate(user=user)
> + resp = self.client.get(self.api_url())
> + self.assertEqual(status.HTTP_200_OK, resp.status_code)
> + self.assertEqual(1, len(resp.data))
> + self.assertEqual(user.username, resp.data[0]['name'])
> + self.assertEqual(user.username, resp.data[0]['username'])
> + self.assertEqual(user.email, resp.data[0]['email'])
> +
> + def test_unlinked_user(self):
> + defaults.patch_author_person.save()
> + user = create_user()
> + self.client.force_authenticate(user=user)
> + resp = self.client.get(self.api_url())
> + self.assertEqual(status.HTTP_200_OK, resp.status_code)
> + self.assertEqual(2, len(resp.data))
> + self.assertEqual(
> + defaults.patch_author_person.name, resp.data[0]['name'])
> + self.assertEqual('', resp.data[0]['username'])
> +
> + def test_readonly(self):
> + defaults.project.save()
> + user = create_maintainer(defaults.project)
> + user.is_superuser = True
> + user.save()
> + self.client.force_authenticate(user=user)
> +
> + resp = self.client.delete(self.api_url(1))
> + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code)
> +
> + resp = self.client.patch(self.api_url(1), {'email': 'foo at f.com'})
> + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code)
> +
> + resp = self.client.post(self.api_url(), {'email': 'foo at f.com'})
> + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code)
> diff --git a/patchwork/views/rest_api.py b/patchwork/views/rest_api.py
> index 8c207ff..c7e511f 100644
> --- a/patchwork/views/rest_api.py
> +++ b/patchwork/views/rest_api.py
> @@ -19,8 +19,7 @@
>
> from django.conf import settings
>
> -from patchwork.models import Project
> -from patchwork.rest_serializers import ProjectSerializer
> +from patchwork.rest_serializers import ProjectSerializer, PersonSerializer
>
> from rest_framework import permissions
> from rest_framework.pagination import PageNumberPagination
> @@ -67,12 +66,33 @@ class PatchworkPermission(permissions.BasePermission):
> return obj.is_editable(request.user)
>
>
> -class ProjectViewSet(ModelViewSet):
> +class AuthenticatedReadOnly(permissions.BasePermission):
> + def has_permission(self, request, view):
> + authenticated = request.user.is_authenticated()
> + return authenticated and request.method in permissions.SAFE_METHODS
> +
> +
> +class PatchworkViewSet(ModelViewSet):
> + pagination_class = LinkHeaderPagination
> +
> + def get_queryset(self):
> + return self.serializer_class.Meta.model.objects.all()
> +
> +
> +class PeopleViewSet(PatchworkViewSet):
> + permission_classes = (AuthenticatedReadOnly, )
> + serializer_class = PersonSerializer
> +
> + def get_queryset(self):
> + qs = super(PeopleViewSet, self).get_queryset()
> + return qs.select_related('user__username')
> +
> +
> +class ProjectViewSet(PatchworkViewSet):
> permission_classes = (PatchworkPermission, )
> - queryset = Project.objects.all()
> serializer_class = ProjectSerializer
> - pagination_class = LinkHeaderPagination
>
>
> router = DefaultRouter()
> +router.register('people', PeopleViewSet, 'person')
> router.register('projects', ProjectViewSet, 'project')
> --
> 2.7.4
>
> _______________________________________________
> Patchwork mailing list
> Patchwork at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/patchwork
More information about the Patchwork
mailing list