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 <[email protected]> > --- > 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()) > + > + > [email protected](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': '[email protected]'}) > + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) > + > + resp = self.client.post(self.api_url(), {'email': '[email protected]'}) > + 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 > [email protected] > https://lists.ozlabs.org/listinfo/patchwork _______________________________________________ Patchwork mailing list [email protected] https://lists.ozlabs.org/listinfo/patchwork
