mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-10 06:04:15 +00:00
Include favicons and thumbnails in REST API (#763)
* Include favicons and thumbnails in REST API * Fix serialization for custom endpoints
This commit is contained in:
parent
b28352fb28
commit
380f5ed19c
5 changed files with 100 additions and 6 deletions
|
@ -52,7 +52,7 @@ class BookmarkViewSet(
|
||||||
return Bookmark.objects.all().filter(owner=user)
|
return Bookmark.objects.all().filter(owner=user)
|
||||||
|
|
||||||
def get_serializer_context(self):
|
def get_serializer_context(self):
|
||||||
return {"user": self.request.user}
|
return {"request": self.request, "user": self.request.user}
|
||||||
|
|
||||||
@action(methods=["get"], detail=False)
|
@action(methods=["get"], detail=False)
|
||||||
def archived(self, request):
|
def archived(self, request):
|
||||||
|
@ -60,8 +60,8 @@ class BookmarkViewSet(
|
||||||
search = BookmarkSearch.from_request(request.GET)
|
search = BookmarkSearch.from_request(request.GET)
|
||||||
query_set = queries.query_archived_bookmarks(user, user.profile, search)
|
query_set = queries.query_archived_bookmarks(user, user.profile, search)
|
||||||
page = self.paginate_queryset(query_set)
|
page = self.paginate_queryset(query_set)
|
||||||
serializer = self.get_serializer_class()
|
serializer = self.get_serializer(page, many=True)
|
||||||
data = serializer(page, many=True).data
|
data = serializer.data
|
||||||
return self.get_paginated_response(data)
|
return self.get_paginated_response(data)
|
||||||
|
|
||||||
@action(methods=["get"], detail=False)
|
@action(methods=["get"], detail=False)
|
||||||
|
@ -73,8 +73,8 @@ class BookmarkViewSet(
|
||||||
user, request.user_profile, search, public_only
|
user, request.user_profile, search, public_only
|
||||||
)
|
)
|
||||||
page = self.paginate_queryset(query_set)
|
page = self.paginate_queryset(query_set)
|
||||||
serializer = self.get_serializer_class()
|
serializer = self.get_serializer(page, many=True)
|
||||||
data = serializer(page, many=True).data
|
data = serializer.data
|
||||||
return self.get_paginated_response(data)
|
return self.get_paginated_response(data)
|
||||||
|
|
||||||
@action(methods=["post"], detail=True)
|
@action(methods=["post"], detail=True)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db.models import prefetch_related_objects
|
from django.db.models import prefetch_related_objects
|
||||||
|
from django.templatetags.static import static
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.serializers import ListSerializer
|
from rest_framework.serializers import ListSerializer
|
||||||
|
|
||||||
|
@ -31,6 +32,8 @@ class BookmarkSerializer(serializers.ModelSerializer):
|
||||||
"website_title",
|
"website_title",
|
||||||
"website_description",
|
"website_description",
|
||||||
"web_archive_snapshot_url",
|
"web_archive_snapshot_url",
|
||||||
|
"favicon_url",
|
||||||
|
"preview_image_url",
|
||||||
"is_archived",
|
"is_archived",
|
||||||
"unread",
|
"unread",
|
||||||
"shared",
|
"shared",
|
||||||
|
@ -42,6 +45,8 @@ class BookmarkSerializer(serializers.ModelSerializer):
|
||||||
"website_title",
|
"website_title",
|
||||||
"website_description",
|
"website_description",
|
||||||
"web_archive_snapshot_url",
|
"web_archive_snapshot_url",
|
||||||
|
"favicon_url",
|
||||||
|
"preview_image_url",
|
||||||
"date_added",
|
"date_added",
|
||||||
"date_modified",
|
"date_modified",
|
||||||
]
|
]
|
||||||
|
@ -56,6 +61,24 @@ class BookmarkSerializer(serializers.ModelSerializer):
|
||||||
shared = serializers.BooleanField(required=False, default=False)
|
shared = serializers.BooleanField(required=False, default=False)
|
||||||
# Override readonly tag_names property to allow passing a list of tag names to create/update
|
# Override readonly tag_names property to allow passing a list of tag names to create/update
|
||||||
tag_names = TagListField(required=False, default=[])
|
tag_names = TagListField(required=False, default=[])
|
||||||
|
favicon_url = serializers.SerializerMethodField()
|
||||||
|
preview_image_url = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_favicon_url(self, obj: Bookmark):
|
||||||
|
if not obj.favicon_file:
|
||||||
|
return None
|
||||||
|
request = self.context.get("request")
|
||||||
|
favicon_file_path = static(obj.favicon_file)
|
||||||
|
favicon_url = request.build_absolute_uri(favicon_file_path)
|
||||||
|
return favicon_url
|
||||||
|
|
||||||
|
def get_preview_image_url(self, obj: Bookmark):
|
||||||
|
if not obj.preview_image_file:
|
||||||
|
return None
|
||||||
|
request = self.context.get("request")
|
||||||
|
preview_image_file_path = static(obj.preview_image_file)
|
||||||
|
preview_image_url = request.build_absolute_uri(preview_image_file_path)
|
||||||
|
return preview_image_url
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
bookmark = Bookmark()
|
bookmark = Bookmark()
|
||||||
|
|
|
@ -87,6 +87,8 @@ class BookmarkFactoryMixin:
|
||||||
shared: bool = False,
|
shared: bool = False,
|
||||||
with_tags: bool = False,
|
with_tags: bool = False,
|
||||||
with_web_archive_snapshot_url: bool = False,
|
with_web_archive_snapshot_url: bool = False,
|
||||||
|
with_favicon_file: bool = False,
|
||||||
|
with_preview_image_file: bool = False,
|
||||||
user: User = None,
|
user: User = None,
|
||||||
):
|
):
|
||||||
user = user or self.get_or_create_test_user()
|
user = user or self.get_or_create_test_user()
|
||||||
|
@ -118,6 +120,12 @@ class BookmarkFactoryMixin:
|
||||||
web_archive_snapshot_url = ""
|
web_archive_snapshot_url = ""
|
||||||
if with_web_archive_snapshot_url:
|
if with_web_archive_snapshot_url:
|
||||||
web_archive_snapshot_url = f"https://web.archive.org/web/{i}"
|
web_archive_snapshot_url = f"https://web.archive.org/web/{i}"
|
||||||
|
favicon_file = ""
|
||||||
|
if with_favicon_file:
|
||||||
|
favicon_file = f"favicon_{i}.png"
|
||||||
|
preview_image_file = ""
|
||||||
|
if with_preview_image_file:
|
||||||
|
preview_image_file = f"preview_image_{i}.png"
|
||||||
bookmark = self.setup_bookmark(
|
bookmark = self.setup_bookmark(
|
||||||
url=url,
|
url=url,
|
||||||
title=title,
|
title=title,
|
||||||
|
@ -126,6 +134,8 @@ class BookmarkFactoryMixin:
|
||||||
shared=shared,
|
shared=shared,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
web_archive_snapshot_url=web_archive_snapshot_url,
|
web_archive_snapshot_url=web_archive_snapshot_url,
|
||||||
|
favicon_file=favicon_file,
|
||||||
|
preview_image_file=preview_image_file,
|
||||||
user=user,
|
user=user,
|
||||||
)
|
)
|
||||||
bookmarks.append(bookmark)
|
bookmarks.append(bookmark)
|
||||||
|
|
|
@ -36,6 +36,16 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||||
expectation["website_title"] = bookmark.website_title
|
expectation["website_title"] = bookmark.website_title
|
||||||
expectation["website_description"] = bookmark.website_description
|
expectation["website_description"] = bookmark.website_description
|
||||||
expectation["web_archive_snapshot_url"] = bookmark.web_archive_snapshot_url
|
expectation["web_archive_snapshot_url"] = bookmark.web_archive_snapshot_url
|
||||||
|
expectation["favicon_url"] = (
|
||||||
|
f"http://testserver/static/{bookmark.favicon_file}"
|
||||||
|
if bookmark.favicon_file
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
expectation["preview_image_url"] = (
|
||||||
|
f"http://testserver/static/{bookmark.preview_image_file}"
|
||||||
|
if bookmark.preview_image_file
|
||||||
|
else None
|
||||||
|
)
|
||||||
expectation["is_archived"] = bookmark.is_archived
|
expectation["is_archived"] = bookmark.is_archived
|
||||||
expectation["unread"] = bookmark.unread
|
expectation["unread"] = bookmark.unread
|
||||||
expectation["shared"] = bookmark.shared
|
expectation["shared"] = bookmark.shared
|
||||||
|
@ -65,7 +75,11 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||||
def test_list_bookmarks_with_more_details(self):
|
def test_list_bookmarks_with_more_details(self):
|
||||||
self.authenticate()
|
self.authenticate()
|
||||||
bookmarks = self.setup_numbered_bookmarks(
|
bookmarks = self.setup_numbered_bookmarks(
|
||||||
5, with_tags=True, with_web_archive_snapshot_url=True
|
5,
|
||||||
|
with_tags=True,
|
||||||
|
with_web_archive_snapshot_url=True,
|
||||||
|
with_favicon_file=True,
|
||||||
|
with_preview_image_file=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.get(
|
response = self.get(
|
||||||
|
@ -171,6 +185,23 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||||
)
|
)
|
||||||
self.assertBookmarkListEqual(response.data["results"], archived_bookmarks)
|
self.assertBookmarkListEqual(response.data["results"], archived_bookmarks)
|
||||||
|
|
||||||
|
def test_list_archived_bookmarks_with_more_details(self):
|
||||||
|
self.authenticate()
|
||||||
|
archived_bookmarks = self.setup_numbered_bookmarks(
|
||||||
|
5,
|
||||||
|
archived=True,
|
||||||
|
with_tags=True,
|
||||||
|
with_web_archive_snapshot_url=True,
|
||||||
|
with_favicon_file=True,
|
||||||
|
with_preview_image_file=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.get(
|
||||||
|
reverse("bookmarks:bookmark-archived"),
|
||||||
|
expected_status_code=status.HTTP_200_OK,
|
||||||
|
)
|
||||||
|
self.assertBookmarkListEqual(response.data["results"], archived_bookmarks)
|
||||||
|
|
||||||
def test_list_archived_bookmarks_should_filter_by_query(self):
|
def test_list_archived_bookmarks_should_filter_by_query(self):
|
||||||
self.authenticate()
|
self.authenticate()
|
||||||
search_value = self.get_random_string()
|
search_value = self.get_random_string()
|
||||||
|
@ -220,6 +251,26 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||||
)
|
)
|
||||||
self.assertBookmarkListEqual(response.data["results"], shared_bookmarks)
|
self.assertBookmarkListEqual(response.data["results"], shared_bookmarks)
|
||||||
|
|
||||||
|
def test_list_shared_bookmarks_with_more_details(self):
|
||||||
|
self.authenticate()
|
||||||
|
|
||||||
|
other_user = self.setup_user(enable_sharing=True)
|
||||||
|
shared_bookmarks = self.setup_numbered_bookmarks(
|
||||||
|
5,
|
||||||
|
shared=True,
|
||||||
|
user=other_user,
|
||||||
|
with_tags=True,
|
||||||
|
with_web_archive_snapshot_url=True,
|
||||||
|
with_favicon_file=True,
|
||||||
|
with_preview_image_file=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.get(
|
||||||
|
reverse("bookmarks:bookmark-shared"),
|
||||||
|
expected_status_code=status.HTTP_200_OK,
|
||||||
|
)
|
||||||
|
self.assertBookmarkListEqual(response.data["results"], shared_bookmarks)
|
||||||
|
|
||||||
def test_list_only_publicly_shared_bookmarks_when_not_logged_in(self):
|
def test_list_only_publicly_shared_bookmarks_when_not_logged_in(self):
|
||||||
user1 = self.setup_user(enable_sharing=True, enable_public_sharing=True)
|
user1 = self.setup_user(enable_sharing=True, enable_public_sharing=True)
|
||||||
user2 = self.setup_user(enable_sharing=True)
|
user2 = self.setup_user(enable_sharing=True)
|
||||||
|
@ -701,6 +752,8 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||||
url="https://example.com",
|
url="https://example.com",
|
||||||
title="Example title",
|
title="Example title",
|
||||||
description="Example description",
|
description="Example description",
|
||||||
|
favicon_file="favicon.png",
|
||||||
|
preview_image_file="preview.png",
|
||||||
)
|
)
|
||||||
|
|
||||||
url = reverse("bookmarks:bookmark-check")
|
url = reverse("bookmarks:bookmark-check")
|
||||||
|
@ -715,6 +768,12 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||||
self.assertEqual(bookmark.url, bookmark_data["url"])
|
self.assertEqual(bookmark.url, bookmark_data["url"])
|
||||||
self.assertEqual(bookmark.title, bookmark_data["title"])
|
self.assertEqual(bookmark.title, bookmark_data["title"])
|
||||||
self.assertEqual(bookmark.description, bookmark_data["description"])
|
self.assertEqual(bookmark.description, bookmark_data["description"])
|
||||||
|
self.assertEqual(
|
||||||
|
"http://testserver/static/favicon.png", bookmark_data["favicon_url"]
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
"http://testserver/static/preview.png", bookmark_data["preview_image_url"]
|
||||||
|
)
|
||||||
|
|
||||||
def test_check_returns_existing_metadata_if_url_is_bookmarked(self):
|
def test_check_returns_existing_metadata_if_url_is_bookmarked(self):
|
||||||
self.authenticate()
|
self.authenticate()
|
||||||
|
|
|
@ -49,6 +49,8 @@ Example response:
|
||||||
"website_title": "Website title",
|
"website_title": "Website title",
|
||||||
"website_description": "Website description",
|
"website_description": "Website description",
|
||||||
"web_archive_snapshot_url": "https://web.archive.org/web/20200926094623/https://example.com",
|
"web_archive_snapshot_url": "https://web.archive.org/web/20200926094623/https://example.com",
|
||||||
|
"favicon_url": "http://127.0.0.1:8000/static/https_example_com.png",
|
||||||
|
"preview_image_url": "http://127.0.0.1:8000/static/0ac5c53db923727765216a3a58e70522.jpg",
|
||||||
"is_archived": false,
|
"is_archived": false,
|
||||||
"unread": false,
|
"unread": false,
|
||||||
"shared": false,
|
"shared": false,
|
||||||
|
|
Loading…
Reference in a new issue