From 8bbd5777e0470221b71f68fd560f39c5e04209cb Mon Sep 17 00:00:00 2001 From: haeunia Date: Sun, 17 Nov 2024 16:57:00 +0900 Subject: [PATCH 1/5] =?UTF-8?q?reformer=20career=EC=99=80=20freelancer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reformer_profile_serializer.py | 51 +++++++- users/urls.py | 48 ++++++++ .../reformer_career_view/__init__.py | 0 .../reformer_career_create_list_view.py | 70 +++++++++++ .../reformer_career_document_view.py | 51 ++++++++ .../reformer_career_view.py | 112 ++++++++++++++++++ .../reformer_freelancer_view/__init__.py | 0 .../reformer_freelancer_create_list_view.py | 70 +++++++++++ .../reformer_freelancer_document_view.py | 48 ++++++++ .../reformer_freelancer_view.py | 111 +++++++++++++++++ 10 files changed, 556 insertions(+), 5 deletions(-) create mode 100644 users/views/reformer_view/reformer_career_view/__init__.py create mode 100644 users/views/reformer_view/reformer_career_view/reformer_career_create_list_view.py create mode 100644 users/views/reformer_view/reformer_career_view/reformer_career_document_view.py create mode 100644 users/views/reformer_view/reformer_career_view/reformer_career_view.py create mode 100644 users/views/reformer_view/reformer_freelancer_view/__init__.py create mode 100644 users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_create_list_view.py create mode 100644 users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_document_view.py create mode 100644 users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_view.py diff --git a/users/serializers/reformer_serializer/reformer_profile_serializer.py b/users/serializers/reformer_serializer/reformer_profile_serializer.py index 971ace02..68d2bfae 100644 --- a/users/serializers/reformer_serializer/reformer_profile_serializer.py +++ b/users/serializers/reformer_serializer/reformer_profile_serializer.py @@ -63,15 +63,30 @@ def update(self, instance, validated_data): class ReformerCareerSerializer(serializers.ModelSerializer): + career_uuid = serializers.UUIDField(read_only=True) + proof_document = serializers.FileField(read_only=True) + class Meta: model = ReformerCareer - fields = ["company_name", "department", "period"] + fields = [ + "career_uuid", + "company_name", + "department", + "period", + "proof_document", + ] + def create(self, validated_data): + new_career = ReformerCareer.objects.create( + reformer=self.context.get("reformer"), **validated_data + ) + return new_career -class ReformerFreelancerSerializer(serializers.ModelSerializer): - class Meta: - model = ReformerFreelancer - fields = ["project_name", "description"] + def update(self, instance, validated_data): + for key, value in validated_data.items(): + setattr(instance, key, value) + instance.save() + return instance class ReformerEducationSerializer(serializers.ModelSerializer): @@ -101,6 +116,32 @@ def update(self, instance, validated_data): return instance +class ReformerFreelancerSerializer(serializers.ModelSerializer): + freelancer_uuid = serializers.UUIDField(read_only=True) + proof_document = serializers.FileField(read_only=True) + + class Meta: + model = ReformerFreelancer + fields = [ + "freelancer_uuid", + "project_name", + "description", + "proof_document", + ] + + def create(self, validated_data): + new_freelancer = ReformerFreelancer.objects.create( + reformer=self.context.get("reformer"), **validated_data + ) + return new_freelancer + + def update(self, instance, validated_data): + for key, value in validated_data.items(): + setattr(instance, key, value) + instance.save() + return instance + + class ReformerProfileSerializer(serializers.Serializer): nickname = serializers.SerializerMethodField() education = ReformerEducationSerializer(many=True, required=False) diff --git a/users/urls.py b/users/urls.py index 9982f87b..9cf77599 100644 --- a/users/urls.py +++ b/users/urls.py @@ -9,6 +9,15 @@ from users.views.reformer_view.reformer_awards_view.reformer_awards_view import ( ReformerAwardsView, ) +from users.views.reformer_view.reformer_career_view.reformer_career_create_list_view import ( + ReformerCareerCreateListView, +) +from users.views.reformer_view.reformer_career_view.reformer_career_document_view import ( + ReformerCareerDocumentView, +) +from users.views.reformer_view.reformer_career_view.reformer_career_view import ( + ReformerCareerView, +) from users.views.reformer_view.reformer_certification_view.reformer_certification_create_list_view import ( ReformerCertificationCreateListView, ) @@ -27,6 +36,15 @@ from users.views.reformer_view.reformer_education_view.reformer_education_view import ( ReformerEducationView, ) +from users.views.reformer_view.reformer_freelancer_view.reformer_freelancer_create_list_view import ( + ReformerFreelancerCreateListView, +) +from users.views.reformer_view.reformer_freelancer_view.reformer_freelancer_document_view import ( + ReformerFreelancerDocumentView, +) +from users.views.reformer_view.reformer_freelancer_view.reformer_freelancer_view import ( + ReformerFreelancerView, +) from users.views.reformer_view.reformer_profile_view import ReformerProfileView from users.views.token_view.token_view import UserTokenRefreshView, UserTokenVerifyView from users.views.user_view.user_auth_view import * @@ -87,5 +105,35 @@ ReformerAwardsDocumentView.as_view(), name="reformer_awards_document", ), + path( + "/reformer/career", + ReformerCareerCreateListView.as_view(), + name="reformer_career", + ), + path( + "/reformer/career/", + ReformerCareerView.as_view(), + name="reformer_career_detail", + ), + path( + "/reformer/career//document", + ReformerCareerDocumentView.as_view(), + name="reformer_career_document", + ), + path( + "/reformer/freelancer", + ReformerFreelancerCreateListView.as_view(), + name="reformer_freelancer", + ), + path( + "/reformer/freelancer/", + ReformerFreelancerView.as_view(), + name="reformer_freelancer_detail", + ), + path( + "/reformer/freelancer//document", + ReformerFreelancerDocumentView.as_view(), + name="reformer_freelancer_document", + ), path("/profile-image", UserImageUploadView.as_view(), name="upload_profile_image"), ] diff --git a/users/views/reformer_view/reformer_career_view/__init__.py b/users/views/reformer_view/reformer_career_view/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/users/views/reformer_view/reformer_career_view/reformer_career_create_list_view.py b/users/views/reformer_view/reformer_career_view/reformer_career_create_list_view.py new file mode 100644 index 00000000..a878e6f0 --- /dev/null +++ b/users/views/reformer_view/reformer_career_view/reformer_career_create_list_view.py @@ -0,0 +1,70 @@ +from django.db import IntegrityError +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from users.models.reformer import Reformer, ReformerCareer +from users.serializers.reformer_serializer.reformer_profile_serializer import ( + ReformerCareerSerializer, +) + + +class ReformerCareerCreateListView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + try: + reformer_career = ReformerCareer.objects.filter( + reformer=request.user.reformer_profile + ) + if not reformer_career: + raise Reformer.DoesNotExist + + serializer = ReformerCareerSerializer(instance=reformer_career, many=True) + return Response(data=serializer.data, status=status.HTTP_200_OK) + except AttributeError as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) + except Reformer.DoesNotExist: + return Response( + data={ + "message": "해당 User가 생성한 Reformer 프로필 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + + def post(self, request): + try: + reformer = Reformer.objects.filter(user=request.user).first() + if not reformer: + raise Reformer.DoesNotExist + + serializer = ReformerCareerSerializer( + data=request.data, context={"reformer": reformer} + ) + if serializer.is_valid(): + serializer.save() + return Response( + data={"message": "successfully created"}, + status=status.HTTP_201_CREATED, + ) + else: + raise ValueError(f"{serializer.errors}") + except (AttributeError, ValueError) as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) + except IntegrityError as e: + return Response( + data={"message": "데이터베이스 무결성 오류"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except Reformer.DoesNotExist: + return Response( + data={ + "message": "해당 User가 생성한 Reformer 프로필 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) diff --git a/users/views/reformer_view/reformer_career_view/reformer_career_document_view.py b/users/views/reformer_view/reformer_career_view/reformer_career_document_view.py new file mode 100644 index 00000000..dea218ae --- /dev/null +++ b/users/views/reformer_view/reformer_career_view/reformer_career_document_view.py @@ -0,0 +1,51 @@ +from django.db import IntegrityError +from rest_framework import status +from rest_framework.exceptions import ValidationError +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from users.models.reformer import ReformerCareer + + +class ReformerCareerDocumentView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + pass + + def post(self, request, **kwargs): + try: + career_uuid = kwargs.get("career_uuid") + reformer_career = ReformerCareer.objects.filter( + career_uuid=career_uuid + ).first() + if not reformer_career: + raise ReformerCareer.DoesNotExist + document_file = request.FILES.get("document") + if not document_file: + raise ValidationError("Document is required") + + reformer_career.proof_document = document_file + reformer_career.save() + + return Response( + data={"message": "Successfully uploaded document"}, + status=status.HTTP_201_CREATED, + ) + except IntegrityError: + return Response( + data={"message": "데이터베이스 무결성 오류"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except ReformerCareer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 경력 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + except ValidationError as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) diff --git a/users/views/reformer_view/reformer_career_view/reformer_career_view.py b/users/views/reformer_view/reformer_career_view/reformer_career_view.py new file mode 100644 index 00000000..f3951e1c --- /dev/null +++ b/users/views/reformer_view/reformer_career_view/reformer_career_view.py @@ -0,0 +1,112 @@ +import os + +from boto3 import client +from botocore import errorfactory +from django.db import IntegrityError, transaction +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from users.models.reformer import ReformerCareer +from users.serializers.reformer_serializer.reformer_profile_serializer import ( + ReformerCareerSerializer, +) + + +class ReformerCareerView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, **kwargs): + try: + career_uuid = self.kwargs.get("career_uuid") + reformer_career = ReformerCareer.objects.filter( + career_uuid=career_uuid + ).first() + if not reformer_career: + raise ReformerCareer.DoesNotExist + + serializer = ReformerCareerSerializer(instance=reformer_career) + return Response(data=serializer.data, status=status.HTTP_200_OK) + except ReformerCareer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 경력 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + + def put(self, request, **kwargs): + try: + career_uuid = kwargs.get("career_uuid") + reformer_career = ReformerCareer.objects.filter( + career_uuid=career_uuid + ).first() + if not reformer_career: + raise ReformerCareer.DoesNotExist + + serializer = ReformerCareerSerializer( + instance=reformer_career, data=request.data, partial=True + ) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + data={"message": "successfully updated"}, status=status.HTTP_200_OK + ) + else: + raise ValueError(f"{serializer.errors}") + except (AttributeError, ValueError) as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) + except ReformerCareer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 경력 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + except Exception as e: + return Response( + data={"message": f"예상치 못한 오류가 발생했습니다: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + def delete(self, request, **kwargs): + try: + career_uuid = kwargs.get("career_uuid") + reformer_career = ReformerCareer.objects.filter( + career_uuid=career_uuid + ).first() + if not reformer_career: + raise ReformerCareer.DoesNotExist + + with transaction.atomic(): + if reformer_career.proof_document: # 증명 서류가 존재한다면, 삭제 + s3 = client("s3") + s3.delete_object( + Bucket=os.getenv("AWS_STORAGE_BUCKET_NAME"), + Key=reformer_career.proof_document.name, + ) + + reformer_career.delete() + return Response( + data={"message": "successfully deleted"}, status=status.HTTP_200_OK + ) + except IntegrityError: + return Response( + data={"message": "데이터베이스 무결성 오류"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except errorfactory.ClientError: + return Response( + data={"message": "백엔드에서 설정한 버킷이 존재하지 않습니다."}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + except ReformerCareer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 경력 내역 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) diff --git a/users/views/reformer_view/reformer_freelancer_view/__init__.py b/users/views/reformer_view/reformer_freelancer_view/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_create_list_view.py b/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_create_list_view.py new file mode 100644 index 00000000..d6e6f572 --- /dev/null +++ b/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_create_list_view.py @@ -0,0 +1,70 @@ +from django.db import IntegrityError +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from users.models.reformer import Reformer, ReformerFreelancer +from users.serializers.reformer_serializer.reformer_profile_serializer import ( + ReformerFreelancerSerializer, +) + + +class ReformerFreelancerCreateListView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + try: + reformer_freelancer = ReformerFreelancer.objects.filter( + reformer=request.user.reformer_profile + ) + if not reformer_freelancer: + raise Reformer.DoesNotExist + + serializer = ReformerFreelancerSerializer( + instance=reformer_freelancer, many=True + ) + return Response(data=serializer.data, status=status.HTTP_200_OK) + except AttributeError as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) + except Reformer.DoesNotExist: + return Response( + data={ + "message": "해당 User가 생성한 Reformer 프로필 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + + def post(self, request): + try: + reformer = Reformer.objects.filter(user=request.user).first() + if not reformer: + raise Reformer.DoesNotExist("Reformer 프로필 정보가 없습니다.") + + serializer = ReformerFreelancerSerializer( + data=request.data, context={"reformer": reformer} + ) + if serializer.is_valid(): + serializer.save() + return Response( + data={"message": "successfully created"}, + status=status.HTTP_201_CREATED, + ) + else: + raise ValueError(f"{serializer.errors}") + except (AttributeError, ValueError) as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) + except IntegrityError as e: + return Response( + data={"message": "데이터베이스 무결성 오류"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except Reformer.DoesNotExist as e: + return Response( + data={"message": str(e)}, + status=status.HTTP_404_NOT_FOUND, + ) diff --git a/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_document_view.py b/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_document_view.py new file mode 100644 index 00000000..c26eb6b5 --- /dev/null +++ b/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_document_view.py @@ -0,0 +1,48 @@ +from django.db import IntegrityError +from rest_framework import status +from rest_framework.exceptions import ValidationError +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from users.models.reformer import ReformerFreelancer + + +class ReformerFreelancerDocumentView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request, **kwargs): + try: + freelancer_uuid = kwargs.get("freelancer_uuid") + reformer_freelancer = ReformerFreelancer.objects.filter( + freelancer_uuid=freelancer_uuid + ).first() + if not reformer_freelancer: + raise ReformerFreelancer.DoesNotExist + document_file = request.FILES.get("document") + if not document_file: + raise ValidationError("Document is required") + + reformer_freelancer.proof_document = document_file + reformer_freelancer.save() + + return Response( + data={"message": "Successfully uploaded document"}, + status=status.HTTP_201_CREATED, + ) + except IntegrityError: + return Response( + data={"message": "데이터베이스 무결성 오류"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except ReformerFreelancer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 프리랜서/외주 경력 내역 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + except ValidationError as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) diff --git a/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_view.py b/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_view.py new file mode 100644 index 00000000..d741eb62 --- /dev/null +++ b/users/views/reformer_view/reformer_freelancer_view/reformer_freelancer_view.py @@ -0,0 +1,111 @@ +import os + +from boto3 import client +from botocore import errorfactory +from django.db import IntegrityError, transaction +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from users.models.reformer import ReformerFreelancer +from users.serializers.reformer_serializer.reformer_profile_serializer import ( + ReformerFreelancerSerializer, +) + + +class ReformerFreelancerView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, **kwargs): + try: + freelancer_uuid = kwargs.get("freelancer_uuid") + reformer_freelancer = ReformerFreelancer.objects.filter( + freelancer_uuid=freelancer_uuid + ).first() + if not reformer_freelancer: + raise ReformerFreelancer.DoesNotExist + + serializer = ReformerFreelancerSerializer(instance=reformer_freelancer) + return Response(data=serializer.data, status=status.HTTP_200_OK) + except ReformerFreelancer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 프리랜서/외주 경력 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + except Exception as e: + return Response( + data={"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + def put(self, request, **kwargs): + try: + freelancer_uuid = kwargs.get("freelancer_uuid") + reformer_freelancer = ReformerFreelancer.objects.filter( + freelancer_uuid=freelancer_uuid + ).first() + if not reformer_freelancer: + raise ReformerFreelancer.DoesNotExist + + serializer = ReformerFreelancerSerializer( + instance=reformer_freelancer, data=request.data, partial=True + ) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + data={"message": "successfully updated"}, status=status.HTTP_200_OK + ) + else: + raise ValueError(f"{serializer.errors}") + except (AttributeError, ValueError) as e: + return Response( + data={"message": str(e)}, status=status.HTTP_400_BAD_REQUEST + ) + except ReformerFreelancer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 프리랜서/외주 경력 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) + + def delete(self, request, **kwargs): + try: + freelancer_uuid = kwargs.get("freelancer_uuid") + reformer_freelancer = ReformerFreelancer.objects.filter( + freelancer_uuid=freelancer_uuid + ).first() + if not reformer_freelancer: + raise ReformerFreelancer.DoesNotExist + + with transaction.atomic(): + if reformer_freelancer.proof_document: # 증명 서류가 존재한다면, 삭제 + s3 = client("s3") + s3.delete_object( + Bucket=os.getenv("AWS_STORAGE_BUCKET_NAME"), + Key=reformer_freelancer.proof_document.name, + ) + + reformer_freelancer.delete() + return Response( + data={"message": "successfully deleted"}, status=status.HTTP_200_OK + ) + except IntegrityError: + return Response( + data={"message": "데이터베이스 무결성 오류"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except errorfactory.ClientError: + return Response( + data={"message": "백엔드에서 설정한 버킷이 존재하지 않습니다."}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + except ReformerFreelancer.DoesNotExist: + return Response( + data={ + "message": "해당 UUID에 해당하는 리포머 프리랜서/외주 경력 정보가 존재하지 않습니다." + }, + status=status.HTTP_404_NOT_FOUND, + ) From 2e0895fb0b647f97ae0ff17408b599fd37428428 Mon Sep 17 00:00:00 2001 From: haeunia Date: Tue, 19 Nov 2024 23:45:37 +0900 Subject: [PATCH 2/5] . --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a61f21cd..c852d08d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # 클라우드 아키텍쳐 + ![image](https://github.com/user-attachments/assets/910f79f5-80f7-4420-9897-088adaacec95) # 프로젝트 아키텍쳐 From 67ef9471fd7d288435f050c3f4a46020af5176d0 Mon Sep 17 00:00:00 2001 From: haeunia Date: Wed, 20 Nov 2024 14:08:46 +0900 Subject: [PATCH 3/5] . --- README.md | 1 + users/tests.py | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index c852d08d..47257612 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # 클라우드 아키텍쳐 + ![image](https://github.com/user-attachments/assets/910f79f5-80f7-4420-9897-088adaacec95) # 프로젝트 아키텍쳐 diff --git a/users/tests.py b/users/tests.py index fc915556..b10517fa 100644 --- a/users/tests.py +++ b/users/tests.py @@ -183,6 +183,7 @@ def test_user_delete(self): self.assertEqual(user_count, 0) + class ReformerTestCase(APITestCase): def setUp(self): From e57ec1ed0c2b2aaf5e4400d2495e59cfb314ba44 Mon Sep 17 00:00:00 2001 From: haeunia Date: Sat, 23 Nov 2024 14:20:55 +0900 Subject: [PATCH 4/5] a --- users/tests.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/users/tests.py b/users/tests.py index b10517fa..c133db51 100644 --- a/users/tests.py +++ b/users/tests.py @@ -75,6 +75,7 @@ def test_user_login(self): self.assertEqual(response.status_code, 200) self.assertIn("access", response.data) + self.assertIn("refresh", response.data) def test_user_logout(self): @@ -183,7 +184,6 @@ def test_user_delete(self): self.assertEqual(user_count, 0) - class ReformerTestCase(APITestCase): def setUp(self): @@ -283,17 +283,73 @@ def test_reformer_create(self): self.assertEqual(reformer.first().reformer_career.count(), 2) self.assertEqual(reformer.first().reformer_freelancer.count(), 2) + # 4. 이미 등록된 리포머를 등록하려고 하는 경우, 에러가 뜨는지 확인 + + def test_reformer_get_list(self): # 1. 로그인 상태 response = self.client.post( path="/api/user/login", data=self.login_request_data, format="json" ) self.access_token = response.data["access"] - self.refresh_token = response.data["refresh"] self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) + # 2. 리포머 목록 요청 + response = self.client.get(path="/api/user/reformer", format="json") + self.assertEqual(response.status_code, 200) + + # 3. 응답 데이터 확인 + reformer_list = response.data + self.assertIsInstance(reformer_list, list) + self.assertGreaterEqual(len(reformer_list), 1) # 최소 1개의 리포머가 있어야 함 + self.assertEqual(reformer_list[0]["user"]["email"], self.test_user.email) + self.assertEqual(reformer_list[0]["reformer_link"], None) # 기본값 확인 가능 + self.assertEqual(reformer_list[0]["reformer_area"], None) + + + + + def test_reformer_update(self): - pass + # 1. 로그인 상태 + response = self.client.post( + path="/api/user/login", data=self.login_request_data, format="json" + ) + self.access_token = response.data["access"] + self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) + + # 2. 리포머 정보 업데이트 + update_data = { + "reformer_link": "https://updated-link.com", + "reformer_area": "seoul gangnam", + } + response = self.client.put( + path="/api/user/reformer", data=update_data, format="json" + ) + self.assertEqual(response.status_code, 200) + + # 3. 업데이트된 정보 확인 + reformer = Reformer.objects.get(user=self.test_user) + self.assertEqual(reformer.reformer_link, "https://updated-link.com") + self.assertEqual(reformer.reformer_area, "seoul gangnam") + def test_reformer_delete(self): - pass + # 1. 로그인 상태 + response = self.client.post( + path="/api/user/login", data=self.login_request_data, format="json" + ) + self.access_token = response.data["access"] + self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) + + # 2. 리포머 삭제 요청 + response = self.client.delete(path="/api/user/reformer", format="json") + self.assertEqual(response.status_code, 204) # 삭제 성공 시 일반적으로 204 반환 + + # 3. 삭제 확인 + reformer_count = Reformer.objects.filter(user=self.test_user).count() + self.assertEqual(reformer_count, 0) + + # 4. 삭제된 상태에서 재요청 시 에러 확인 + response = self.client.delete(path="/api/user/reformer", format="json") + self.assertEqual(response.status_code, 404) # 이미 삭제된 리소스 \ No newline at end of file From 5b5716e7808bdbbb0d43c6fc072d29d062458d64 Mon Sep 17 00:00:00 2001 From: haeunia Date: Sat, 30 Nov 2024 15:20:47 +0900 Subject: [PATCH 5/5] =?UTF-8?q?#156=20reformer=20api=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- market/tests.py | 1 + users/tests.py | 252 +++++++++--------- .../reformer_education_view.py | 18 +- 3 files changed, 141 insertions(+), 130 deletions(-) diff --git a/market/tests.py b/market/tests.py index aef58a33..41823d42 100644 --- a/market/tests.py +++ b/market/tests.py @@ -213,6 +213,7 @@ def test_update_market_info(self): self.assertEqual(market.market_introduce, update_data["market_introduce"]) @patch("market.views.market_view.market_crud_view.client") + def test_delete_market_info(self, mock_boto3_client: MagicMock): # 마켓 삭제 시도 테스트 diff --git a/users/tests.py b/users/tests.py index c133db51..7dc2042b 100644 --- a/users/tests.py +++ b/users/tests.py @@ -189,167 +189,173 @@ class ReformerTestCase(APITestCase): def setUp(self): self.client = APIClient() - # Reformer 관련 API 처리는 반드시 Access token이 필요하므로 로그인 먼저 수행 + # 테스트 사용자 생성 self.test_user = User.objects.create_user( email="test@test.com", password="123123", phone="01012341234", - full_name="hello", + full_name="Test User", nickname="nickname", introduce="hello, django", ) - self.login_request_data = {"email": "test@test.com", "password": "123123"} - def test_reformer_create(self): + # 리포머 생성에 사용할 데이터 + self.reformer_data = { + "reformer_link": "https://test.com", + "reformer_area": "seoul", + "education": [ + {"school": "Test School", "major": "Design", "academic_status": "Graduated"}, + ], + "certification": [ + {"name": "Certification 1", "issuing_authority": "Authority 1"}, + ], + "awards": [ + {"competition": "Competition 1", "prize": "First Prize"}, + ], + "career": [ + {"company_name": "Company 1", "department": "IT", "period": "2 years"}, + ], + "freelancer": [ + {"project_name": "Project 1", "description": "Freelance work description"}, + ], + } - # 1. 일반 로그인 상태 - response = self.client.post( - path="/api/user/login", data=self.login_request_data, format="json" - ) + # 로그인 및 인증 헤더 설정 + self.login_and_set_token() + + def login_and_set_token(self): + # 로그인 수행 및 인증 헤더 설정 + login_data = {"email": "test@test.com", "password": "123123"} + response = self.client.post(path="/api/user/login", data=login_data, format="json") + self.assertEqual(response.status_code, 200, "로그인 실패") self.access_token = response.data["access"] - self.refresh_token = response.data["refresh"] self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) - # 2. 요청 전, 사용자의 권한이 customer 레벨이고, 요청 후 사용자의 권한이 Reformer 레벨로 변경되는지 확인 - user = User.objects.get_user_by_email( - self.login_request_data.get("email") - ).first() - self.assertEqual(user.role, "customer") - - response = self.client.post( - path="/api/user/reformer", - data={ - "reformer_link": "https://www.naver.com", - "reformer_area": "seoul yongsan", - "education": [ - { - "school": "Test school", - "major": "Fashion Design", - "academic_status": "Graduated", - }, - { - "school": "Another University", - "major": "Art", - "academic_status": "Graduated", - }, - ], - "certification": [ - {"name": "test cert", "issuing_authority": "asdfasdfasdf"}, - {"name": "test cert 2", "issuing_authority": "gjdofisjgfdg"}, - ], - "awards": [ - {"competition": "어쩌구저쩌구대회", "prize": "멍때리기대회1등"}, - {"competition": "어쩌구저쩌구대회2", "prize": "웃음참기대회1등"}, - ], - "career": [ - { - "company_name": "sdiojfgoijfsgf", - "department": "디자인", - "period": "3년", - }, - { - "company_name": "pjgfhjfghjgoijfsgfthgdrthgdrth", - "department": "디자인", - "period": "6개월", - }, - ], - "freelancer": [ - { - "project_name": "oubj89todjirng", - "description": "이런저런 일을 했습니다", - }, - { - "project_name": "ㅁㄴㅇㄻㄴㅇㄻㄴㅇㄹ", - "description": "이런저런 일을 했습니다 2", - }, - ], - }, - format="json", - ) + def test_reformer_create(self): + before_user = User.objects.get(email="test@test.com") + self.assertEqual(before_user.role, "customer") + # 리포머 생성 테스트, 리포머를 생성하는 과정에서 사용자의 역할이 "customer"에서 "reformer"로 변경되는지 확인 + response = self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") self.assertEqual(response.status_code, 201) - user = User.objects.get_user_by_email( - self.login_request_data.get("email") - ).first() - self.assertEqual( - user.role, "reformer" - ) # 사용자의 권한이 reformer로 바뀌었는지 확인 + self.assertEqual(response.data["message"], "successfully created") - # 3. Reformer 객체와 관련 객체가 생성되었는지 확인 - reformer = Reformer.objects.filter(user=user) - self.assertEqual(reformer.count(), 1) - self.assertEqual(reformer.first().reformer_education.count(), 2) - self.assertEqual(reformer.first().reformer_certification.count(), 2) - self.assertEqual(reformer.first().reformer_awards.count(), 2) - self.assertEqual(reformer.first().reformer_career.count(), 2) - self.assertEqual(reformer.first().reformer_freelancer.count(), 2) + after_user = User.objects.get(email="test@test.com") + self.assertEqual(after_user.role, "reformer") - # 4. 이미 등록된 리포머를 등록하려고 하는 경우, 에러가 뜨는지 확인 + # 데이터 검증 + reformer = Reformer.objects.filter(user=self.test_user).first() + self.assertIsNotNone(reformer) + self.assertEqual(reformer.reformer_area, self.reformer_data["reformer_area"]) + def test_reformer_duplicate_creation(self): + # 첫 번째 생성 + self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") - def test_reformer_get_list(self): - # 1. 로그인 상태 - response = self.client.post( - path="/api/user/login", data=self.login_request_data, format="json" - ) - self.access_token = response.data["access"] - self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) - - # 2. 리포머 목록 요청 - response = self.client.get(path="/api/user/reformer", format="json") - self.assertEqual(response.status_code, 200) - - # 3. 응답 데이터 확인 - reformer_list = response.data - self.assertIsInstance(reformer_list, list) - self.assertGreaterEqual(len(reformer_list), 1) # 최소 1개의 리포머가 있어야 함 - self.assertEqual(reformer_list[0]["user"]["email"], self.test_user.email) - self.assertEqual(reformer_list[0]["reformer_link"], None) # 기본값 확인 가능 - self.assertEqual(reformer_list[0]["reformer_area"], None) + # 중복 생성 시도를 하면 에러가 발생함 + response = self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") + self.assertEqual(response.status_code, 400) + self.assertIn("message", response.data) + def test_reformer_create_missing_fields(self): + # 필요한 필드가 누락된 경우 에러가 발생함 + incomplete_data = { + "reformer_link": "https://test.com", # 'reformer_area' 필드 누락 + # "reformer_area": "seoul", + } + response = self.client.post(path="/api/user/reformer", data=incomplete_data, format="json") + self.assertEqual(response.status_code, 400) + self.assertIn("message", response.data) # 에러 메시지 확인 + def test_reformer_get(self): + # 리포머 생성 + self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") + # 리포머 데이터 조회 + response = self.client.get(path="/api/user/reformer", format="json") + self.assertEqual(response.status_code, 200) + # 데이터 검증 + response_data = response.data + self.assertEqual(response_data["reformer_link"], self.reformer_data["reformer_link"]) + self.assertEqual(response_data["reformer_area"], self.reformer_data["reformer_area"]) def test_reformer_update(self): - # 1. 로그인 상태 - response = self.client.post( - path="/api/user/login", data=self.login_request_data, format="json" - ) - self.access_token = response.data["access"] - self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) + # 리포머 생성 + self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") - # 2. 리포머 정보 업데이트 + # 리포머 데이터 업데이트 update_data = { "reformer_link": "https://updated-link.com", "reformer_area": "seoul gangnam", } - response = self.client.put( - path="/api/user/reformer", data=update_data, format="json" - ) + response = self.client.put(path="/api/user/reformer", data=update_data, format="json") self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["message"], "successfully updated") - # 3. 업데이트된 정보 확인 + # 업데이트된 데이터 검증 reformer = Reformer.objects.get(user=self.test_user) - self.assertEqual(reformer.reformer_link, "https://updated-link.com") - self.assertEqual(reformer.reformer_area, "seoul gangnam") + self.assertEqual(reformer.reformer_link, update_data["reformer_link"]) + self.assertEqual(reformer.reformer_area, update_data["reformer_area"]) + + def test_reformer_update_without_permissions(self): + # 리포머 역할이 아닌 사용자가 리포머 정보를 업데이트 하려고 할 때 403 오류 발생 + non_reformer_user = User.objects.create_user( + email="nonreformer@test.com", password="123123" + ) + self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.get_access_token(non_reformer_user)) + update_data = {"reformer_link": "https://new-link.com", "reformer_area": "busan"} + response = self.client.put(path="/api/user/reformer", data=update_data, format="json") + + self.assertEqual(response.status_code, 403) + self.assertIn("detail", response.data) # 권한 오류 메시지 확인 def test_reformer_delete(self): - # 1. 로그인 상태 - response = self.client.post( - path="/api/user/login", data=self.login_request_data, format="json" - ) - self.access_token = response.data["access"] - self.client.credentials(HTTP_AUTHORIZATION="Bearer " + self.access_token) + # 리포머 생성 + self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") - # 2. 리포머 삭제 요청 + # 리포머 삭제 response = self.client.delete(path="/api/user/reformer", format="json") - self.assertEqual(response.status_code, 204) # 삭제 성공 시 일반적으로 204 반환 + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["message"], "successfully deleted") - # 3. 삭제 확인 + # 데이터 삭제 확인 reformer_count = Reformer.objects.filter(user=self.test_user).count() self.assertEqual(reformer_count, 0) - # 4. 삭제된 상태에서 재요청 시 에러 확인 + def test_reformer_delete_nonexistent(self): + # 존재하지 않는 리포머 삭제를 시도하면 에러 response = self.client.delete(path="/api/user/reformer", format="json") - self.assertEqual(response.status_code, 404) # 이미 삭제된 리소스 \ No newline at end of file + self.assertEqual(response.status_code, 404) + self.assertIn("message", response.data) + + def test_reformer_delete_after_update(self): + # 리포머 생성 + self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") + + # 리포머 삭제 + self.client.delete(path="/api/user/reformer", format="json") + + # 삭제된 리포머가 데이터베이스에서 완전히 삭제되었는지 확인 + reformer_count = Reformer.objects.filter(user=self.test_user).count() + self.assertEqual(reformer_count, 0) + + # 리포머 재생성 시도 + response = self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") + self.assertEqual(response.status_code, 201) + + # 다시 생성된 리포머가 존재하는지 확인 + reformer = Reformer.objects.filter(user=self.test_user).first() + self.assertIsNotNone(reformer) + self.assertEqual(reformer.reformer_link, self.reformer_data["reformer_link"]) + + def test_reformer_role_check(self): + # 리포머 생성 후 사용자 역할이 reformer가 되는 것을 다시 확인 + self.client.post(path="/api/user/reformer", data=self.reformer_data, format="json") + user = User.objects.get(email="test@test.com") + self.assertEqual(user.role, "reformer") + + def get_access_token(self, user: User): + login_data = {"email": user.email, "password": "123123"} + response = self.client.post(path="/api/user/login", data=login_data, format="json") + return response.data["access"] \ No newline at end of file diff --git a/users/views/reformer_view/reformer_education_view/reformer_education_view.py b/users/views/reformer_view/reformer_education_view/reformer_education_view.py index b48ea830..b27391fc 100644 --- a/users/views/reformer_view/reformer_education_view/reformer_education_view.py +++ b/users/views/reformer_view/reformer_education_view/reformer_education_view.py @@ -77,11 +77,15 @@ def delete(self, request, **kwargs): raise ReformerEducation.DoesNotExist with transaction.atomic(): - s3 = client("s3") - s3.delete_object( - Bucket=os.getenv("AWS_STORAGE_BUCKET_NAME"), - Key=reformer_education.proof_document.name, - ) + if ( + reformer_education.proof_document + ): # 증명 서류가 존재한다면, 삭제 + s3 = client("s3") + s3.delete_object( + Bucket=os.getenv("AWS_STORAGE_BUCKET_NAME"), + Key=reformer_education.proof_document.name, + ) + reformer_education.delete() return Response( data={"message": "successfully deleted"}, status=status.HTTP_200_OK @@ -99,7 +103,7 @@ def delete(self, request, **kwargs): except ReformerEducation.DoesNotExist: return Response( data={ - "message": "해당 UUID에 해당하는 리포머 학력 정보가 존재하지 않습니다." + "message": "해당 UUID에 해당하는 리포머 학력 내역 정보가 존재하지 않습니다." }, status=status.HTTP_404_NOT_FOUND, - ) + ) \ No newline at end of file