Commit 654239dc by Greg Price

Merge pull request #2890 from edx/gprice/user-api-with-prefs

Augment user API to allow easier access to user preferences
parents 779d0aab bbd6f788
from django.contrib.auth.models import User
from django.core.validators import RegexValidator
from django.db import models
class UserPreference(models.Model):
"""A user's preference, stored as generic text to be processed by client"""
user = models.ForeignKey(User, db_index=True, related_name="+")
key = models.CharField(max_length=255, db_index=True)
KEY_REGEX = r"[-_a-zA-Z0-9]+"
user = models.ForeignKey(User, db_index=True, related_name="preferences")
key = models.CharField(max_length=255, db_index=True, validators=[RegexValidator(KEY_REGEX)])
value = models.TextField()
class Meta: # pylint: disable=missing-docstring
......
......@@ -6,15 +6,19 @@ from user_api.models import UserPreference
class UserSerializer(serializers.HyperlinkedModelSerializer):
name = serializers.SerializerMethodField("get_name")
preferences = serializers.SerializerMethodField("get_preferences")
def get_name(self, user):
profile = UserProfile.objects.get(user=user)
return profile.name
def get_preferences(self, user):
return dict([(pref.key, pref.value) for pref in user.preferences.all()])
class Meta:
model = User
# This list is the minimal set required by the notification service
fields = ("id", "email", "name", "username")
fields = ("id", "email", "name", "username", "preferences")
read_only_fields = ("id", "email", "username")
......
......@@ -66,7 +66,11 @@ class ApiTestCase(TestCase):
def assertUserIsValid(self, user):
"""Assert that the given user result is valid"""
self.assertItemsEqual(user.keys(), ["email", "id", "name", "username", "url"])
self.assertItemsEqual(user.keys(), ["email", "id", "name", "username", "preferences", "url"])
self.assertItemsEqual(
user["preferences"].items(),
[(pref.key, pref.value) for pref in self.prefs if pref.user.id == user["id"]]
)
self.assertSelfReferential(user)
def assertPrefIsValid(self, pref):
......@@ -221,6 +225,11 @@ class UserViewSetTest(UserApiTestCase):
"id": user.id,
"name": user.profile.name,
"username": user.username,
"preferences": dict([
(user_pref.key, user_pref.value)
for user_pref in self.prefs
if user_pref.user == user
]),
"url": uri
}
)
......@@ -352,6 +361,11 @@ class UserPreferenceViewSetTest(UserApiTestCase):
"id": pref.user.id,
"name": pref.user.profile.name,
"username": pref.user.username,
"preferences": dict([
(user_pref.key, user_pref.value)
for user_pref in self.prefs
if user_pref.user == pref.user
]),
"url": self.get_uri_for_user(pref.user),
},
"key": pref.key,
......@@ -359,3 +373,59 @@ class UserPreferenceViewSetTest(UserApiTestCase):
"url": uri,
}
)
class PreferenceUsersListViewTest(UserApiTestCase):
LIST_URI = "/user_api/v1/preferences/key0/users/"
def test_options(self):
self.assertAllowedMethods(self.LIST_URI, ["OPTIONS", "GET", "HEAD"])
def test_put_not_allowed(self):
self.assertHttpMethodNotAllowed(self.request_with_auth("put", self.LIST_URI))
def test_patch_not_allowed(self):
raise SkipTest("Django 1.4's test client does not support patch")
def test_delete_not_allowed(self):
self.assertHttpMethodNotAllowed(self.request_with_auth("delete", self.LIST_URI))
def test_unauthorized(self):
self.assertHttpForbidden(self.client.get(self.LIST_URI))
@override_settings(DEBUG=True)
@override_settings(EDX_API_KEY=None)
def test_debug_auth(self):
self.assertHttpOK(self.client.get(self.LIST_URI))
def test_get_basic(self):
result = self.get_json(self.LIST_URI)
self.assertEqual(result["count"], 2)
self.assertIsNone(result["next"])
self.assertIsNone(result["previous"])
users = result["results"]
self.assertEqual(len(users), 2)
for user in users:
self.assertUserIsValid(user)
def test_get_pagination(self):
first_page = self.get_json(self.LIST_URI, data={"page_size": 1})
self.assertEqual(first_page["count"], 2)
first_page_next_uri = first_page["next"]
self.assertIsNone(first_page["previous"])
first_page_users = first_page["results"]
self.assertEqual(len(first_page_users), 1)
second_page = self.get_json(first_page_next_uri)
self.assertEqual(second_page["count"], 2)
self.assertIsNone(second_page["next"])
second_page_prev_uri = second_page["previous"]
second_page_users = second_page["results"]
self.assertEqual(len(second_page_users), 1)
self.assertEqual(self.get_json(second_page_prev_uri), first_page)
for user in first_page_users + second_page_users:
self.assertUserIsValid(user)
all_user_uris = [user["url"] for user in first_page_users + second_page_users]
self.assertEqual(len(set(all_user_uris)), 2)
from django.conf.urls import include, patterns, url
from rest_framework import routers
from user_api import views as user_api_views
from user_api.models import UserPreference
user_api_router = routers.DefaultRouter()
......@@ -9,4 +10,8 @@ user_api_router.register(r'user_prefs', user_api_views.UserPreferenceViewSet)
urlpatterns = patterns(
'',
url(r'^v1/', include(user_api_router.urls)),
url(
r'^v1/preferences/(?P<pref_key>{})/users/$'.format(UserPreference.KEY_REGEX),
user_api_views.PreferenceUsersListView.as_view()
),
)
......@@ -2,6 +2,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import filters
from rest_framework import generics
from rest_framework import permissions
from rest_framework import viewsets
from user_api.serializers import UserSerializer, UserPreferenceSerializer
......@@ -28,7 +29,7 @@ class ApiKeyHeaderPermission(permissions.BasePermission):
class UserViewSet(viewsets.ReadOnlyModelViewSet):
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (ApiKeyHeaderPermission,)
queryset = User.objects.all()
queryset = User.objects.all().prefetch_related("preferences")
serializer_class = UserSerializer
paginate_by = 10
paginate_by_param = "page_size"
......@@ -43,3 +44,14 @@ class UserPreferenceViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = UserPreferenceSerializer
paginate_by = 10
paginate_by_param = "page_size"
class PreferenceUsersListView(generics.ListAPIView):
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (ApiKeyHeaderPermission,)
serializer_class = UserSerializer
paginate_by = 10
paginate_by_param = "page_size"
def get_queryset(self):
return User.objects.filter(preferences__key=self.kwargs["pref_key"]).prefetch_related("preferences")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment