From d0584f8b640b550b4bb9234f35de9764ec9fdda6 Mon Sep 17 00:00:00 2001 From: JinHoooooou Date: Tue, 7 Sep 2021 15:50:03 +0900 Subject: [PATCH 1/5] JH-135 add url to update topic --- v2/src/ctrlfbe/topic_urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/v2/src/ctrlfbe/topic_urls.py b/v2/src/ctrlfbe/topic_urls.py index 7ecf2bb..43748e6 100644 --- a/v2/src/ctrlfbe/topic_urls.py +++ b/v2/src/ctrlfbe/topic_urls.py @@ -7,4 +7,5 @@ urlpatterns = [ path("/pages", PageListView.as_view(), name="page_list"), path("", TopicDetailUpdateDeleteView.as_view(), name="topic_detail"), + path("", TopicDetailUpdateDeleteView.as_view(), name="topic_update"), ] From de86fecb60b3212c92d9bd53b20e7718583e967c Mon Sep 17 00:00:00 2001 From: JinHoooooou Date: Tue, 7 Sep 2021 15:55:57 +0900 Subject: [PATCH 2/5] JH-135 add test to update topic on success --- v2/src/ctrlfbe/serializers.py | 5 +++ v2/src/ctrlfbe/views.py | 9 +++++ v2/src/tests/test_topic_update.py | 58 +++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 v2/src/tests/test_topic_update.py diff --git a/v2/src/ctrlfbe/serializers.py b/v2/src/ctrlfbe/serializers.py index c04a13c..9d8496d 100644 --- a/v2/src/ctrlfbe/serializers.py +++ b/v2/src/ctrlfbe/serializers.py @@ -64,6 +64,11 @@ class Meta: fields = "__all__" read_only_fields = ["id", "created_at"] + def update(self, instance, validated_data): + instance.title = validated_data.get("title", instance.title) + instance.save() + return instance + class PageSerializer(serializers.ModelSerializer): class Meta: diff --git a/v2/src/ctrlfbe/views.py b/v2/src/ctrlfbe/views.py index f3069e9..ede4b23 100644 --- a/v2/src/ctrlfbe/views.py +++ b/v2/src/ctrlfbe/views.py @@ -116,6 +116,15 @@ class TopicDetailUpdateDeleteView(BaseContentView): def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) + def put(self, request, *args, **kwargs): + topic = Topic.objects.get(id=kwargs["topic_id"]) + + serializer = TopicSerializer(topic, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + + return Response(data={"message": "수정되었습니다."}, status=status.HTTP_200_OK) + class PageListView(BaseContentView): parent_model = Topic diff --git a/v2/src/tests/test_topic_update.py b/v2/src/tests/test_topic_update.py new file mode 100644 index 0000000..44e54c0 --- /dev/null +++ b/v2/src/tests/test_topic_update.py @@ -0,0 +1,58 @@ +from ctrlf_auth.models import CtrlfUser +from ctrlf_auth.serializers import LoginSerializer +from ctrlfbe.models import Note, Topic +from django.test import Client, TestCase +from django.urls import reverse +from rest_framework import status + + +class TestTopicUpdate(TestCase): + def setUp(self) -> None: + self.client = Client() + self.topic_owner_data = { + "email": "test@test.com", + "password": "12345", + } + topic_owner = self._create_user(self.topic_owner_data) + self._create_note_topic(topic_owner) + + def _create_user(self, user_data): + return CtrlfUser.objects.create_user(**user_data) + + def _create_note_topic(self, owner): + self.note = Note.objects.create(title="test note title") + self.note.owners.add(owner) + self.topic = Topic.objects.create(note=self.note, title="test topic title") + self.topic.owners.add(owner) + + def _login(self, user_data): + serializer = LoginSerializer() + return serializer.validate(user_data)["token"] + + def _call_api(self, request_body, token=None): + if token: + header = {"HTTP_AUTHORIZATION": f"Bearer {token}"} + else: + header = {} + return self.client.put( + reverse("topics:topic_update", kwargs={"topic_id": self.topic.id}), + data=request_body, + content_type="application/json", + **header, + ) + + def test_should_return_200_when_topic_owner_approve_update(self): + # Given: update topic title이 주어진다. + request_body = {"title": "update topic title"} + # And: note, topic을 생성한 계정으로 로그인 해서 token을 발급 받는다. + token = self._login(user_data=self.topic_owner_data) + + # When: update topic api를 호출한다. + response = self._call_api(request_body, token) + + # Then: status code는 200을 리턴한다. + self.assertEqual(response.status_code, status.HTTP_200_OK) + # And: message는 "수정되었습니다."를 리턴한다. + self.assertEqual(response.data["message"], "수정되었습니다.") + # And: 기존 topic의 title은 "update topic title"로 변경된다. + self.assertEqual(Topic.objects.get(id=1).title, "update topic title") From f9e8876daaead151432b1097bd42eacd4ff4a5df Mon Sep 17 00:00:00 2001 From: JinHoooooou Date: Tue, 7 Sep 2021 16:01:35 +0900 Subject: [PATCH 3/5] JH-135 modify test and add another_user on setUp --- v2/src/tests/test_topic_update.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/v2/src/tests/test_topic_update.py b/v2/src/tests/test_topic_update.py index 44e54c0..422d3ae 100644 --- a/v2/src/tests/test_topic_update.py +++ b/v2/src/tests/test_topic_update.py @@ -13,13 +13,17 @@ def setUp(self) -> None: "email": "test@test.com", "password": "12345", } - topic_owner = self._create_user(self.topic_owner_data) - self._create_note_topic(topic_owner) + self.another_user_data = { + "email": "test22@test.com", + "password": "54321", + } + self.topic_owner = CtrlfUser.objects.create_user(**self.topic_owner_data) + CtrlfUser.objects.create_user(**self.another_user_data) def _create_user(self, user_data): return CtrlfUser.objects.create_user(**user_data) - def _create_note_topic(self, owner): + def _create_topic(self, owner): self.note = Note.objects.create(title="test note title") self.note.owners.add(owner) self.topic = Topic.objects.create(note=self.note, title="test topic title") @@ -44,7 +48,9 @@ def _call_api(self, request_body, token=None): def test_should_return_200_when_topic_owner_approve_update(self): # Given: update topic title이 주어진다. request_body = {"title": "update topic title"} - # And: note, topic을 생성한 계정으로 로그인 해서 token을 발급 받는다. + # And: topic을 생성한다. + self._create_topic(self.topic_owner) + # And: topic 생성한 계정으로 로그인 해서 token을 발급 받는다. token = self._login(user_data=self.topic_owner_data) # When: update topic api를 호출한다. From ab1e008ec7508e35d48e4f621c6912f1d4803817 Mon Sep 17 00:00:00 2001 From: JinHoooooou Date: Tue, 7 Sep 2021 16:07:37 +0900 Subject: [PATCH 4/5] JH-135 add test to update topic on fail --- v2/src/ctrlfbe/views.py | 6 +++++- v2/src/tests/test_topic_update.py | 25 ++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/v2/src/ctrlfbe/views.py b/v2/src/ctrlfbe/views.py index ede4b23..c388bef 100644 --- a/v2/src/ctrlfbe/views.py +++ b/v2/src/ctrlfbe/views.py @@ -108,7 +108,7 @@ def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) -class TopicDetailUpdateDeleteView(BaseContentView): +class TopicDetailUpdateDeleteView(CtrlfAuthenticationMixin, BaseContentView): parent_model = Topic serializer = TopicSerializer @@ -117,8 +117,12 @@ def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) def put(self, request, *args, **kwargs): + ctrlf_user = self._ctrlf_authentication(request) topic = Topic.objects.get(id=kwargs["topic_id"]) + if not topic.owners.filter(id=ctrlf_user.id).exists(): + return Response(data={"message": "권한이 없습니다."}, status=status.HTTP_401_UNAUTHORIZED) + serializer = TopicSerializer(topic, data=request.data, partial=True) if serializer.is_valid(): serializer.save() diff --git a/v2/src/tests/test_topic_update.py b/v2/src/tests/test_topic_update.py index 422d3ae..1fbf21b 100644 --- a/v2/src/tests/test_topic_update.py +++ b/v2/src/tests/test_topic_update.py @@ -20,9 +20,6 @@ def setUp(self) -> None: self.topic_owner = CtrlfUser.objects.create_user(**self.topic_owner_data) CtrlfUser.objects.create_user(**self.another_user_data) - def _create_user(self, user_data): - return CtrlfUser.objects.create_user(**user_data) - def _create_topic(self, owner): self.note = Note.objects.create(title="test note title") self.note.owners.add(owner) @@ -51,10 +48,10 @@ def test_should_return_200_when_topic_owner_approve_update(self): # And: topic을 생성한다. self._create_topic(self.topic_owner) # And: topic 생성한 계정으로 로그인 해서 token을 발급 받는다. - token = self._login(user_data=self.topic_owner_data) + topic_owner_token = self._login(user_data=self.topic_owner_data) # When: update topic api를 호출한다. - response = self._call_api(request_body, token) + response = self._call_api(request_body, topic_owner_token) # Then: status code는 200을 리턴한다. self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -62,3 +59,21 @@ def test_should_return_200_when_topic_owner_approve_update(self): self.assertEqual(response.data["message"], "수정되었습니다.") # And: 기존 topic의 title은 "update topic title"로 변경된다. self.assertEqual(Topic.objects.get(id=1).title, "update topic title") + + def test_should_return_401_when_another_user_approve_update(self): + # Given: update topic title이 주어진다 + request_body = {"title": "update topic title"} + # And: topic을 생성한다. + self._create_topic(self.topic_owner) + # And: topic을 생성한 계정과 다른 계정으로 로그인하여 token을 발급받는다. + another_user_token = self._login(user_data=self.another_user_data) + + # When: update topic api를 호출한다. + response = self._call_api(request_body, another_user_token) + + # Then: status code는 401을 리턴한다. + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + # And: message는 "권한이 없습니다."를 리턴한다. + self.assertEqual(response.data["message"], "권한이 없습니다.") + # And: 기존 topic title은 그대로 "test topic title"이다. + self.assertEqual(Topic.objects.get(id=1).title, "test topic title") From 9c8979633014563b4a83710ec261003b2b9a61d2 Mon Sep 17 00:00:00 2001 From: JinHoooooou Date: Tue, 7 Sep 2021 16:10:42 +0900 Subject: [PATCH 5/5] JH-135 add swagger --- v2/src/ctrlfbe/serializers.py | 4 ++++ v2/src/ctrlfbe/swagger.py | 8 ++++++++ v2/src/ctrlfbe/views.py | 2 ++ 3 files changed, 14 insertions(+) diff --git a/v2/src/ctrlfbe/serializers.py b/v2/src/ctrlfbe/serializers.py index 9d8496d..3386e59 100644 --- a/v2/src/ctrlfbe/serializers.py +++ b/v2/src/ctrlfbe/serializers.py @@ -54,6 +54,10 @@ class NoteCreateRequestBodySerializer(serializers.Serializer): content = serializers.CharField() +class TopicUpdateRequestBodySerializer(serializers.Serializer): + title = serializers.CharField() + + class NoteListQuerySerializer(serializers.Serializer): cursor = serializers.IntegerField() diff --git a/v2/src/ctrlfbe/swagger.py b/v2/src/ctrlfbe/swagger.py index 2c1c223..7917b10 100644 --- a/v2/src/ctrlfbe/swagger.py +++ b/v2/src/ctrlfbe/swagger.py @@ -4,6 +4,7 @@ NoteSerializer, PageSerializer, TopicSerializer, + TopicUpdateRequestBodySerializer, ) SWAGGER_PAGE_LIST_VIEW = { @@ -48,6 +49,13 @@ "tags": ["디테일 화면"], } +SWAGGER_TOPIC_UPDATE_VIEW = { + "operation_summary": "Topic Update API", + "operation_description": "topic_id에 해당하는 Topic의 제목을 수정합니다.", + "request_body": TopicUpdateRequestBodySerializer, + "tags": ["디테일 화면"], +} + SWAGGER_PAGE_DETAIL_VIEW = { "responses": {200: PageSerializer()}, "operation_summary": "Page Detail API", diff --git a/v2/src/ctrlfbe/views.py b/v2/src/ctrlfbe/views.py index c388bef..44e4f41 100644 --- a/v2/src/ctrlfbe/views.py +++ b/v2/src/ctrlfbe/views.py @@ -9,6 +9,7 @@ SWAGGER_PAGE_LIST_VIEW, SWAGGER_TOPIC_DETAIL_VIEW, SWAGGER_TOPIC_LIST_VIEW, + SWAGGER_TOPIC_UPDATE_VIEW, ) from django.db.models import Model from drf_yasg.utils import swagger_auto_schema @@ -116,6 +117,7 @@ class TopicDetailUpdateDeleteView(CtrlfAuthenticationMixin, BaseContentView): def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) + @swagger_auto_schema(**SWAGGER_TOPIC_UPDATE_VIEW) def put(self, request, *args, **kwargs): ctrlf_user = self._ctrlf_authentication(request) topic = Topic.objects.get(id=kwargs["topic_id"])