Skip to content

Commit

Permalink
Add user profile endpoint (#541)
Browse files Browse the repository at this point in the history
* feat: Implement UserProfile serializer and add API endpoint per #457

* chore: Document API addition

* Address review comments

---------

Co-authored-by: fkulla <mail@florian.direct>
  • Loading branch information
sissbruecker and fkulla committed Oct 1, 2023
1 parent 41f79e3 commit 3e4f08f
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 3 deletions.
9 changes: 8 additions & 1 deletion bookmarks/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework.routers import DefaultRouter

from bookmarks import queries
from bookmarks.api.serializers import BookmarkSerializer, TagSerializer
from bookmarks.api.serializers import BookmarkSerializer, TagSerializer, UserProfileSerializer
from bookmarks.models import Bookmark, BookmarkSearch, Tag, User
from bookmarks.services.bookmarks import archive_bookmark, unarchive_bookmark, website_loader
from bookmarks.services.website_loader import WebsiteMetadata
Expand Down Expand Up @@ -108,6 +108,13 @@ def get_serializer_context(self):
return {'user': self.request.user}


class UserViewSet(viewsets.GenericViewSet):
@action(methods=['get'], detail=False)
def profile(self, request):
return Response(UserProfileSerializer(request.user.profile).data)


router = DefaultRouter()
router.register(r'bookmarks', BookmarkViewSet, basename='bookmark')
router.register(r'tags', TagViewSet, basename='tag')
router.register(r'user', UserViewSet, basename='user')
20 changes: 19 additions & 1 deletion bookmarks/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework import serializers
from rest_framework.serializers import ListSerializer

from bookmarks.models import Bookmark, Tag, build_tag_string
from bookmarks.models import Bookmark, Tag, build_tag_string, UserProfile
from bookmarks.services.bookmarks import create_bookmark, update_bookmark
from bookmarks.services.tags import get_or_create_tag

Expand Down Expand Up @@ -89,3 +89,21 @@ class Meta:

def create(self, validated_data):
return get_or_create_tag(validated_data['name'], self.context['user'])


class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = [
"theme",
"bookmark_date_display",
"bookmark_link_target",
"web_archive_integration",
"tag_search",
"enable_sharing",
"enable_public_sharing",
"enable_favicons",
"display_url",
"permanent_notes",
"search_preferences",
]
49 changes: 48 additions & 1 deletion bookmarks/tests/test_bookmarks_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
from django.urls import reverse
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.response import Response

from bookmarks.models import Bookmark
from bookmarks.models import Bookmark, BookmarkSearch, UserProfile
from bookmarks.services import website_loader
from bookmarks.services.website_loader import WebsiteMetadata
from bookmarks.tests.helpers import LinkdingApiTestCase, BookmarkFactoryMixin
Expand Down Expand Up @@ -644,3 +645,49 @@ def test_can_only_access_own_bookmarks(self):
check_url = urllib.parse.quote_plus(inaccessible_bookmark.url)
response = self.get(f'{url}?url={check_url}', expected_status_code=status.HTTP_200_OK)
self.assertIsNone(response.data['bookmark'])

def assertUserProfile(self, response: Response, profile: UserProfile):
self.assertEqual(response.data['theme'], profile.theme)
self.assertEqual(response.data['bookmark_date_display'], profile.bookmark_date_display)
self.assertEqual(response.data['bookmark_link_target'], profile.bookmark_link_target)
self.assertEqual(response.data['web_archive_integration'], profile.web_archive_integration)
self.assertEqual(response.data['tag_search'], profile.tag_search)
self.assertEqual(response.data['enable_sharing'], profile.enable_sharing)
self.assertEqual(response.data['enable_public_sharing'], profile.enable_public_sharing)
self.assertEqual(response.data['enable_favicons'], profile.enable_favicons)
self.assertEqual(response.data['display_url'], profile.display_url)
self.assertEqual(response.data['permanent_notes'], profile.permanent_notes)
self.assertEqual(response.data['search_preferences'], profile.search_preferences)

def test_user_profile(self):
self.authenticate()

# default profile
profile = self.user.profile
url = reverse('bookmarks:user-profile')
response = self.get(url, expected_status_code=status.HTTP_200_OK)

self.assertUserProfile(response, profile)

# update profile
profile.theme = 'dark'
profile.bookmark_date_display = 'absolute'
profile.bookmark_link_target = '_self'
profile.web_archive_integration = 'enabled'
profile.tag_search = 'lax'
profile.enable_sharing = True
profile.enable_public_sharing = True
profile.enable_favicons = True
profile.display_url = True
profile.permanent_notes = True
profile.search_preferences = {
'sort': BookmarkSearch.SORT_TITLE_ASC,
'shared': BookmarkSearch.FILTER_SHARED_OFF,
'unread': BookmarkSearch.FILTER_UNREAD_YES,
}
profile.save()

url = reverse('bookmarks:user-profile')
response = self.get(url, expected_status_code=status.HTTP_200_OK)

self.assertUserProfile(response, profile)
8 changes: 8 additions & 0 deletions bookmarks/tests/test_bookmarks_api_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,11 @@ def test_check_requires_authentication(self):

self.authenticate()
self.get(f'{url}?url={check_url}', expected_status_code=status.HTTP_200_OK)

def test_user_profile_requires_authentication(self):
url = reverse('bookmarks:user-profile')

self.get(url, expected_status_code=status.HTTP_401_UNAUTHORIZED)

self.authenticate()
self.get(url, expected_status_code=status.HTTP_200_OK)
32 changes: 32 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,35 @@ Example payload:
"name": "example"
}
```

### User

**Profile**

```
GET /api/user/profile/
```

User preferences.

Example response:

```json
{
"theme": "auto",
"bookmark_date_display": "relative",
"bookmark_link_target": "_blank",
"web_archive_integration": "enabled",
"tag_search": "lax",
"enable_sharing": true,
"enable_public_sharing": true,
"enable_favicons": false,
"display_url": false,
"permanent_notes": false,
"search_preferences": {
"sort": "title_asc",
"shared": "off",
"unread": "off"
}
}
```

0 comments on commit 3e4f08f

Please sign in to comment.