From 68c3c27b38a5d3c2bb327dbcc4cc9cfbf17c5bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20I=C3=9Fbr=C3=BCcker?= Date: Thu, 26 May 2022 04:15:13 +0200 Subject: [PATCH] Add PATCH support to bookmarks endpoint (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sascha Ißbrücker --- bookmarks/api/serializers.py | 14 +++++--- bookmarks/tests/helpers.py | 5 +++ bookmarks/tests/test_bookmarks_api.py | 50 +++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/bookmarks/api/serializers.py b/bookmarks/api/serializers.py index 13e6570c..2f495010 100644 --- a/bookmarks/api/serializers.py +++ b/bookmarks/api/serializers.py @@ -48,10 +48,16 @@ def create(self, validated_data): return create_bookmark(bookmark, tag_string, self.context['user']) def update(self, instance: Bookmark, validated_data): - instance.url = validated_data['url'] - instance.title = validated_data['title'] - instance.description = validated_data['description'] - tag_string = build_tag_string(validated_data['tag_names']) + # Update fields if they were provided in the payload + for key in ['url', 'title', 'description']: + if key in validated_data: + setattr(instance, key, validated_data[key]) + + # Use tag string from payload, or use bookmark's current tags as fallback + tag_string = build_tag_string(instance.tag_names) + if 'tag_names' in validated_data: + tag_string = build_tag_string(validated_data['tag_names']) + return update_bookmark(instance, tag_string, self.context['user']) diff --git a/bookmarks/tests/helpers.py b/bookmarks/tests/helpers.py index 8266cbdc..a85e7277 100644 --- a/bookmarks/tests/helpers.py +++ b/bookmarks/tests/helpers.py @@ -83,6 +83,11 @@ def put(self, url, data=None, expected_status_code=status.HTTP_200_OK): self.assertEqual(response.status_code, expected_status_code) return response + def patch(self, url, data=None, expected_status_code=status.HTTP_200_OK): + response = self.client.patch(url, data, format='json') + self.assertEqual(response.status_code, expected_status_code) + return response + def delete(self, url, expected_status_code=status.HTTP_200_OK): response = self.client.delete(url) self.assertEqual(response.status_code, expected_status_code) diff --git a/bookmarks/tests/test_bookmarks_api.py b/bookmarks/tests/test_bookmarks_api.py index 9b9f6b2f..7fc8eecd 100644 --- a/bookmarks/tests/test_bookmarks_api.py +++ b/bookmarks/tests/test_bookmarks_api.py @@ -131,6 +131,56 @@ def test_update_bookmark(self): updated_bookmark = Bookmark.objects.get(id=self.bookmark1.id) self.assertEqual(updated_bookmark.url, data['url']) + def test_update_bookmark_fails_without_required_fields(self): + data = {'title': 'https://example.com/'} + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.put(url, data, expected_status_code=status.HTTP_400_BAD_REQUEST) + + def test_update_bookmark_with_minimal_payload_clears_all_fields(self): + data = {'url': 'https://example.com/'} + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.put(url, data, expected_status_code=status.HTTP_200_OK) + updated_bookmark = Bookmark.objects.get(id=self.bookmark1.id) + self.assertEqual(updated_bookmark.url, data['url']) + self.assertEqual(updated_bookmark.title, '') + self.assertEqual(updated_bookmark.description, '') + self.assertEqual(updated_bookmark.tag_names, []) + + def test_patch_bookmark(self): + data = {'url': 'https://example.com'} + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.patch(url, data, expected_status_code=status.HTTP_200_OK) + self.bookmark1.refresh_from_db() + self.assertEqual(self.bookmark1.url, data['url']) + + data = {'title': 'Updated title'} + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.patch(url, data, expected_status_code=status.HTTP_200_OK) + self.bookmark1.refresh_from_db() + self.assertEqual(self.bookmark1.title, data['title']) + + data = {'description': 'Updated description'} + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.patch(url, data, expected_status_code=status.HTTP_200_OK) + self.bookmark1.refresh_from_db() + self.assertEqual(self.bookmark1.description, data['description']) + + data = {'tag_names': ['updated-tag-1', 'updated-tag-2']} + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.patch(url, data, expected_status_code=status.HTTP_200_OK) + self.bookmark1.refresh_from_db() + tag_names = [tag.name for tag in self.bookmark1.tags.all()] + self.assertListEqual(tag_names, ['updated-tag-1', 'updated-tag-2']) + + def test_patch_with_empty_payload_does_not_modify_bookmark(self): + url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) + self.patch(url, {}, expected_status_code=status.HTTP_200_OK) + updated_bookmark = Bookmark.objects.get(id=self.bookmark1.id) + self.assertEqual(updated_bookmark.url, self.bookmark1.url) + self.assertEqual(updated_bookmark.title, self.bookmark1.title) + self.assertEqual(updated_bookmark.description, self.bookmark1.description) + self.assertListEqual(updated_bookmark.tag_names, self.bookmark1.tag_names) + def test_delete_bookmark(self): url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id]) self.delete(url, expected_status_code=status.HTTP_204_NO_CONTENT)