From 29206d920f83b8a187df74ec87a0a612ca9766ae Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 9 May 2024 15:21:25 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=88=98=EC=A0=95=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/member/MemberController.kt | 15 +++++++++++++++ .../presentation/member/dto/MemberDtos.kt | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/main/kotlin/com/petqua/presentation/member/MemberController.kt b/src/main/kotlin/com/petqua/presentation/member/MemberController.kt index 463f07eb..b1fdf902 100644 --- a/src/main/kotlin/com/petqua/presentation/member/MemberController.kt +++ b/src/main/kotlin/com/petqua/presentation/member/MemberController.kt @@ -8,6 +8,7 @@ import com.petqua.domain.auth.LoginMember import com.petqua.domain.auth.SignUpGuest import com.petqua.presentation.member.dto.MemberAddProfileRequest import com.petqua.presentation.member.dto.MemberSignUpRequest +import com.petqua.presentation.member.dto.UpdateProfileRequest import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.security.SecurityRequirement @@ -19,6 +20,7 @@ import org.springframework.http.HttpStatus.CREATED import org.springframework.http.ResponseCookie import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -81,4 +83,17 @@ class MemberController( memberService.addProfile(command) return ResponseEntity.noContent().build() } + + @Operation(summary = "회원 프로필 수정 API", description = "회원의 프로필 정보를 수정합니다") + @ApiResponse(responseCode = "204", description = "회원 프로필 수정 성공") + @SecurityRequirement(name = ACCESS_TOKEN_SECURITY_SCHEME_KEY) + @PatchMapping("/profiles") + fun updateProfile( + @Auth loginMember: LoginMember, + @RequestBody request: UpdateProfileRequest, + ): ResponseEntity { + val command = request.toCommand(loginMember.memberId) + memberService.updateProfile(command) + return ResponseEntity.noContent().build() + } } diff --git a/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt b/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt index 1335d662..f335b544 100644 --- a/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt +++ b/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt @@ -3,6 +3,7 @@ package com.petqua.presentation.member.dto import com.petqua.application.member.dto.MemberAddProfileCommand import com.petqua.application.member.dto.MemberSignUpCommand import com.petqua.application.member.dto.PetFishAddCommand +import com.petqua.application.member.dto.UpdateProfileCommand import io.swagger.v3.oas.annotations.media.Schema import java.time.YearMonth @@ -89,3 +90,19 @@ data class PetFishAddRequest( ) val count: Int, ) + +data class UpdateProfileRequest( + @Schema( + description = "닉네임", + example = "펫쿠아" + ) + val nickname: String, +) { + + fun toCommand(memberId: Long): UpdateProfileCommand { + return UpdateProfileCommand( + memberId = memberId, + nickname = nickname, + ) + } +} \ No newline at end of file From f685b9f564d66c05717985244471fee1565394a2 Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 9 May 2024 15:22:38 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=88=98=EC=A0=95=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../petqua/application/member/MemberService.kt | 18 ++++++++++++++++++ .../application/member/dto/MemberDtos.kt | 5 +++++ .../exception/member/MemberExceptionType.kt | 1 + 3 files changed, 24 insertions(+) diff --git a/src/main/kotlin/com/petqua/application/member/MemberService.kt b/src/main/kotlin/com/petqua/application/member/MemberService.kt index 044d352b..f5f5a660 100644 --- a/src/main/kotlin/com/petqua/application/member/MemberService.kt +++ b/src/main/kotlin/com/petqua/application/member/MemberService.kt @@ -2,6 +2,7 @@ package com.petqua.application.member import com.petqua.application.member.dto.MemberAddProfileCommand import com.petqua.application.member.dto.MemberSignUpCommand +import com.petqua.application.member.dto.UpdateProfileCommand import com.petqua.application.token.AuthTokenInfo import com.petqua.application.token.TokenService import com.petqua.common.domain.findActiveByIdOrThrow @@ -19,6 +20,8 @@ import com.petqua.domain.member.nickname.NicknameWordRepository import com.petqua.domain.policy.bannedword.BannedWordRepository import com.petqua.domain.policy.bannedword.BannedWords import com.petqua.exception.member.MemberException +import com.petqua.exception.member.MemberExceptionType +import com.petqua.exception.member.MemberExceptionType.ALREADY_EXIST_NICKNAME import com.petqua.exception.member.MemberExceptionType.FAILED_NICKNAME_GENERATION import com.petqua.exception.member.MemberExceptionType.HAS_SIGNED_UP_MEMBER import com.petqua.exception.member.MemberExceptionType.NOT_FOUND_MEMBER @@ -88,4 +91,19 @@ class MemberService( val bannedWords = BannedWords(bannedWordRepository.findAll()) bannedWords.validateContainingBannedWord(name) } + + fun updateProfile(command: UpdateProfileCommand) { + validateNickname(command.nickname) + val member = memberRepository.findActiveByIdOrThrow(command.memberId) { + MemberException(NOT_FOUND_MEMBER) + } + member.updateNickname(Nickname.from(command.nickname)) + } + + private fun validateNickname(nickname: String) { + validateContainingBannedWord(nickname) + throwExceptionWhen(memberRepository.existsMemberByNickname(Nickname.from(nickname))) { + MemberException(ALREADY_EXIST_NICKNAME) + } + } } diff --git a/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt b/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt index 53d7941a..7ad2e3ed 100644 --- a/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt +++ b/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt @@ -51,3 +51,8 @@ data class PetFishAddCommand( ) } } + +data class UpdateProfileCommand( + val memberId: Long, + val nickname: String +) diff --git a/src/main/kotlin/com/petqua/exception/member/MemberExceptionType.kt b/src/main/kotlin/com/petqua/exception/member/MemberExceptionType.kt index dbe9edda..9de13b67 100644 --- a/src/main/kotlin/com/petqua/exception/member/MemberExceptionType.kt +++ b/src/main/kotlin/com/petqua/exception/member/MemberExceptionType.kt @@ -25,6 +25,7 @@ enum class MemberExceptionType( INVALID_MEMBER_NICKNAME(BAD_REQUEST, "M16", "유효하지 않은 회원 닉네임입니다."), CONTAINING_BANNED_WORD_NAME(BAD_REQUEST, "M20", "이름에 금지 단어를 포함할 수 없습니다."), + ALREADY_EXIST_NICKNAME(BAD_REQUEST, "M21", "이미 사용 중인 닉네임입니다."), INVALID_MEMBER_STATE(INTERNAL_SERVER_ERROR, "M90", "유효하지 않은 회원 상태입니다."), FAILED_NICKNAME_GENERATION(LOOP_DETECTED, "M91", "서버에서 회원 닉네임을 생성하지 못했습니다.") From 44adc8b024f650d0ac5a516e017e9f873914ab83 Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 9 May 2024 15:24:59 +0900 Subject: [PATCH 3/7] =?UTF-8?q?test:=20=ED=9A=8C=EC=9B=90=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=88=98=EC=A0=95=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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 --- .../member/MemberControllerSteps.kt | 19 +++++ .../member/MemberControllerTest.kt | 76 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/test/kotlin/com/petqua/presentation/member/MemberControllerSteps.kt b/src/test/kotlin/com/petqua/presentation/member/MemberControllerSteps.kt index db4bea23..5ec1978c 100644 --- a/src/test/kotlin/com/petqua/presentation/member/MemberControllerSteps.kt +++ b/src/test/kotlin/com/petqua/presentation/member/MemberControllerSteps.kt @@ -2,6 +2,7 @@ package com.petqua.presentation.member import com.petqua.presentation.member.dto.MemberAddProfileRequest import com.petqua.presentation.member.dto.MemberSignUpRequest +import com.petqua.presentation.member.dto.UpdateProfileRequest import io.restassured.module.kotlin.extensions.Extract import io.restassured.module.kotlin.extensions.Given import io.restassured.module.kotlin.extensions.Then @@ -59,3 +60,21 @@ fun requestAddProfile( response() } } + +fun requestUpdateProfile( + request: UpdateProfileRequest, + accessToken: String, +): Response { + return Given { + log().all() + contentType(APPLICATION_JSON_VALUE) + body(request) + auth().preemptive().oauth2(accessToken) + } When { + patch("/members/profiles") + } Then { + log().all() + } Extract { + response() + } +} diff --git a/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt b/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt index 85760c6f..e2d0c82a 100644 --- a/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt +++ b/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt @@ -2,6 +2,8 @@ package com.petqua.presentation.member import com.petqua.common.domain.findByIdOrThrow import com.petqua.common.exception.ExceptionResponse +import com.petqua.domain.auth.Authority +import com.petqua.domain.auth.token.AuthTokenProvider import com.petqua.domain.fish.FishRepository import com.petqua.domain.member.FishTankRepository import com.petqua.domain.member.MemberRepository @@ -15,11 +17,14 @@ import com.petqua.domain.policy.bannedword.BannedWordRepository import com.petqua.exception.member.MemberExceptionType import com.petqua.exception.member.MemberExceptionType.CONTAINING_BANNED_WORD_NAME import com.petqua.exception.member.MemberExceptionType.HAS_SIGNED_UP_MEMBER +import com.petqua.exception.member.MemberExceptionType.NOT_FOUND_MEMBER import com.petqua.presentation.member.dto.MemberAddProfileRequest import com.petqua.presentation.member.dto.MemberSignUpRequest import com.petqua.presentation.member.dto.PetFishAddRequest +import com.petqua.presentation.member.dto.UpdateProfileRequest import com.petqua.test.ApiTestConfig import com.petqua.test.fixture.fish +import com.petqua.test.fixture.member import com.petqua.test.fixture.memberAddProfileRequest import io.kotest.assertions.assertSoftly import io.kotest.matchers.shouldBe @@ -29,9 +34,11 @@ import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders.AUTHORIZATION import org.springframework.http.HttpStatus.BAD_REQUEST import org.springframework.http.HttpStatus.CREATED +import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.http.HttpStatus.NO_CONTENT import java.time.LocalDate import java.time.YearMonth +import java.util.Date import kotlin.Long.Companion.MIN_VALUE class MemberControllerTest( @@ -41,6 +48,7 @@ class MemberControllerTest( private val fishRepository: FishRepository, private val petFishRepository: PetFishRepository, private val fishTankRepository: FishTankRepository, + private val authTokenProvider: AuthTokenProvider, ) : ApiTestConfig() { init { @@ -307,5 +315,73 @@ class MemberControllerTest( } } } + + Given("회원 프로필을 수정할 때") { + val accessToken = signInAsMember().accessToken + bannedWordRepository.saveAll( + listOf( + BannedWord(word = "금지 단어"), + ) + ) + + When("닉네임을 입력하면") { + val request = UpdateProfileRequest(nickname = "변경된 닉네임") + val response = requestUpdateProfile(request, accessToken) + + Then("회원의 닉네임을 수정한다") { + assertSoftly { + response.statusCode shouldBe NO_CONTENT.value() + + val updatedMember = memberRepository.findByIdOrThrow(1L) + updatedMember.nickname.value shouldBe "변경된 닉네임" + } + } + } + + When("닉네임에 부적절한 단어가 들어가면") { + val request = UpdateProfileRequest(nickname = "금지 단어") + val response = requestUpdateProfile(request, accessToken) + + Then("예외를 응답한다") { + val errorResponse = response.`as`(ExceptionResponse::class.java) + assertSoftly(response) { + statusCode shouldBe BAD_REQUEST.value() + errorResponse.message shouldBe CONTAINING_BANNED_WORD_NAME.errorMessage() + } + } + } + + When("이미 다른 회원이 사용중인 닉네임이라면") { + memberRepository.save(member(nickname = "변경된 닉네임")) + val request = UpdateProfileRequest(nickname = "변경된 닉네임") + val response = requestUpdateProfile(request, accessToken) + + Then("예외를 응답한다") { + val errorResponse = response.`as`(ExceptionResponse::class.java) + assertSoftly(response) { + statusCode shouldBe BAD_REQUEST.value() + errorResponse.message shouldBe MemberExceptionType.ALREADY_EXIST_NICKNAME.errorMessage() + } + } + } + + When("회원이 존재하지 않으면") { + val request = UpdateProfileRequest(nickname = "변경된 닉네임") + val accessTokenOfNoExistMember = authTokenProvider.createLoginAuthToken( + memberId = Long.MAX_VALUE, + authority = Authority.MEMBER, + issuedDate = Date() + ).getAccessToken() + val response = requestUpdateProfile(request, accessTokenOfNoExistMember) + + Then("예외를 응답한다") { + val errorResponse = response.`as`(ExceptionResponse::class.java) + assertSoftly(response) { + statusCode shouldBe NOT_FOUND.value() + errorResponse.message shouldBe NOT_FOUND_MEMBER.errorMessage() + } + } + } + } } } From 92e555a02b6305a9ed2ce6b54f36e3b58030a88b Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 9 May 2024 15:25:16 +0900 Subject: [PATCH 4/7] =?UTF-8?q?test:=20=ED=9A=8C=EC=9B=90=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=88=98=EC=A0=95=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/member/MemberServiceTest.kt | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/test/kotlin/com/petqua/application/member/MemberServiceTest.kt b/src/test/kotlin/com/petqua/application/member/MemberServiceTest.kt index c63b32a7..b7297520 100644 --- a/src/test/kotlin/com/petqua/application/member/MemberServiceTest.kt +++ b/src/test/kotlin/com/petqua/application/member/MemberServiceTest.kt @@ -4,6 +4,7 @@ import com.ninjasquad.springmockk.SpykBean import com.petqua.application.member.dto.MemberAddProfileCommand import com.petqua.application.member.dto.MemberSignUpCommand import com.petqua.application.member.dto.PetFishAddCommand +import com.petqua.application.member.dto.UpdateProfileCommand import com.petqua.domain.auth.AuthCredentialsRepository import com.petqua.domain.fish.FishRepository import com.petqua.domain.member.FishTankRepository @@ -20,6 +21,7 @@ import com.petqua.domain.member.nickname.NicknameWordRepository import com.petqua.domain.policy.bannedword.BannedWord import com.petqua.domain.policy.bannedword.BannedWordRepository import com.petqua.exception.member.MemberException +import com.petqua.exception.member.MemberExceptionType.ALREADY_EXIST_NICKNAME import com.petqua.exception.member.MemberExceptionType.CONTAINING_BANNED_WORD_NAME import com.petqua.exception.member.MemberExceptionType.FAILED_NICKNAME_GENERATION import com.petqua.exception.member.MemberExceptionType.HAS_SIGNED_UP_MEMBER @@ -30,6 +32,7 @@ import com.petqua.exception.member.MemberExceptionType.INVALID_MEMBER_FISH_TANK_ import com.petqua.exception.member.MemberExceptionType.INVALID_MEMBER_PET_FISH import com.petqua.exception.member.MemberExceptionType.INVALID_MEMBER_PET_FISH_COUNT import com.petqua.exception.member.MemberExceptionType.NOT_FOUND_MEMBER +import com.petqua.test.DataCleaner import com.petqua.test.fixture.authCredentials import com.petqua.test.fixture.fish import com.petqua.test.fixture.member @@ -57,6 +60,7 @@ class MemberServiceTest( private val fishRepository: FishRepository, private val petFishRepository: PetFishRepository, private val fishTankRepository: FishTankRepository, + private val dataCleaner: DataCleaner, @SpykBean private val nicknameGenerator: NicknameGenerator, ) : BehaviorSpec({ @@ -444,4 +448,69 @@ class MemberServiceTest( } } } + + Given("프로필 수정을 할 때") { + val member = memberRepository.save(member(nickname = "닉네임")) + bannedWordRepository.save(BannedWord(word = "금지 단어")) + + When("닉네임을 입력하면") { + val command = UpdateProfileCommand( + memberId = member.id, + nickname = "변경된 닉네임", + ) + memberService.updateProfile(command) + + Then("닉네임을 수정한다") { + val updatedMember = memberRepository.findById(member.id).get() + + assertSoftly { + updatedMember.nickname.value shouldBe "변경된 닉네임" + } + } + } + + When("닉네임에 부적절한 단어가 들어가면") { + val command = UpdateProfileCommand( + memberId = member.id, + nickname = "금지 단어", + ) + + Then("예외를 던진다") { + shouldThrow { + memberService.updateProfile(command) + }.exceptionType() shouldBe CONTAINING_BANNED_WORD_NAME + } + } + + When("이미 다른 회원이 사용중인 닉네임이라면") { + memberRepository.save(member(nickname = "변경된 닉네임")) + val command = UpdateProfileCommand( + memberId = member.id, + nickname = "변경된 닉네임", + ) + + Then("예외를 던진다") { + shouldThrow { + memberService.updateProfile(command) + }.exceptionType() shouldBe ALREADY_EXIST_NICKNAME + } + } + + When("회원이 존재하지 않으면") { + val command = UpdateProfileCommand( + memberId = Long.MAX_VALUE, + nickname = "변경된 닉네임", + ) + + Then("예외를 던진다") { + shouldThrow { + memberService.updateProfile(command) + }.exceptionType() shouldBe NOT_FOUND_MEMBER + } + } + } + + afterContainer { + dataCleaner.clean() + } }) From 9cd87bee97f3bf546eb0c7e0b13858d1038829d2 Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 16 May 2024 13:03:26 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=8B=9C=20=EC=A4=91=EB=B3=B5=EC=9D=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/petqua/application/member/MemberService.kt | 11 +++++++---- .../com/petqua/domain/member/MemberRepository.kt | 2 ++ .../com/petqua/presentation/member/dto/MemberDtos.kt | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/petqua/application/member/MemberService.kt b/src/main/kotlin/com/petqua/application/member/MemberService.kt index f5f5a660..5c23ff64 100644 --- a/src/main/kotlin/com/petqua/application/member/MemberService.kt +++ b/src/main/kotlin/com/petqua/application/member/MemberService.kt @@ -20,7 +20,6 @@ import com.petqua.domain.member.nickname.NicknameWordRepository import com.petqua.domain.policy.bannedword.BannedWordRepository import com.petqua.domain.policy.bannedword.BannedWords import com.petqua.exception.member.MemberException -import com.petqua.exception.member.MemberExceptionType import com.petqua.exception.member.MemberExceptionType.ALREADY_EXIST_NICKNAME import com.petqua.exception.member.MemberExceptionType.FAILED_NICKNAME_GENERATION import com.petqua.exception.member.MemberExceptionType.HAS_SIGNED_UP_MEMBER @@ -93,16 +92,20 @@ class MemberService( } fun updateProfile(command: UpdateProfileCommand) { - validateNickname(command.nickname) + validateNickname(command.nickname, command.memberId) val member = memberRepository.findActiveByIdOrThrow(command.memberId) { MemberException(NOT_FOUND_MEMBER) } member.updateNickname(Nickname.from(command.nickname)) } - private fun validateNickname(nickname: String) { + private fun validateNickname(nickname: String, memberId: Long) { validateContainingBannedWord(nickname) - throwExceptionWhen(memberRepository.existsMemberByNickname(Nickname.from(nickname))) { + validateDuplicatedNickname(nickname, memberId) + } + + private fun validateDuplicatedNickname(nickname: String, memberId: Long) { + throwExceptionWhen(memberRepository.existsMemberByNicknameAndIdNot(Nickname.from(nickname), memberId)) { MemberException(ALREADY_EXIST_NICKNAME) } } diff --git a/src/main/kotlin/com/petqua/domain/member/MemberRepository.kt b/src/main/kotlin/com/petqua/domain/member/MemberRepository.kt index 6bc73d96..a0195e6c 100644 --- a/src/main/kotlin/com/petqua/domain/member/MemberRepository.kt +++ b/src/main/kotlin/com/petqua/domain/member/MemberRepository.kt @@ -17,5 +17,7 @@ interface MemberRepository : JpaRepository { fun existsMemberByNickname(nickname: Nickname): Boolean + fun existsMemberByNicknameAndIdNot(nickname: Nickname, id: Long): Boolean + fun existsMemberByAuthCredentialsId(authCredentialsId: Long): Boolean } diff --git a/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt b/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt index f335b544..b52ea114 100644 --- a/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt +++ b/src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt @@ -105,4 +105,4 @@ data class UpdateProfileRequest( nickname = nickname, ) } -} \ No newline at end of file +} From 70c3577019038bc58914c41d460d565afa453f46 Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 16 May 2024 13:07:42 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20command=EA=B0=80=20Nickname?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EA=B0=80=EC=A7=80=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/petqua/application/member/MemberService.kt | 10 +++++----- .../com/petqua/application/member/dto/MemberDtos.kt | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/petqua/application/member/MemberService.kt b/src/main/kotlin/com/petqua/application/member/MemberService.kt index 5c23ff64..ed9441e8 100644 --- a/src/main/kotlin/com/petqua/application/member/MemberService.kt +++ b/src/main/kotlin/com/petqua/application/member/MemberService.kt @@ -96,16 +96,16 @@ class MemberService( val member = memberRepository.findActiveByIdOrThrow(command.memberId) { MemberException(NOT_FOUND_MEMBER) } - member.updateNickname(Nickname.from(command.nickname)) + member.updateNickname(command.nickname) } - private fun validateNickname(nickname: String, memberId: Long) { - validateContainingBannedWord(nickname) + private fun validateNickname(nickname: Nickname, memberId: Long) { + validateContainingBannedWord(nickname.value) validateDuplicatedNickname(nickname, memberId) } - private fun validateDuplicatedNickname(nickname: String, memberId: Long) { - throwExceptionWhen(memberRepository.existsMemberByNicknameAndIdNot(Nickname.from(nickname), memberId)) { + private fun validateDuplicatedNickname(nickname: Nickname, memberId: Long) { + throwExceptionWhen(memberRepository.existsMemberByNicknameAndIdNot(nickname, memberId)) { MemberException(ALREADY_EXIST_NICKNAME) } } diff --git a/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt b/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt index 7ad2e3ed..79185627 100644 --- a/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt +++ b/src/main/kotlin/com/petqua/application/member/dto/MemberDtos.kt @@ -5,6 +5,7 @@ import com.petqua.domain.member.PetFish import com.petqua.domain.member.PetFishCount import com.petqua.domain.member.PetFishSex import com.petqua.domain.member.PetFishes +import com.petqua.domain.member.nickname.Nickname import java.time.YearMonth data class MemberSignUpCommand( @@ -54,5 +55,8 @@ data class PetFishAddCommand( data class UpdateProfileCommand( val memberId: Long, - val nickname: String -) + val nickname: Nickname, +) { + + constructor(memberId: Long, nickname: String) : this(memberId, Nickname.from(nickname)) +} From 5ded6926e763b84065df9521a5f4128bbaecf5b4 Mon Sep 17 00:00:00 2001 From: hgo641 Date: Thu, 16 May 2024 13:24:09 +0900 Subject: [PATCH 7/7] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20API=20=EC=A3=BC=EC=84=9D=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/member/MemberController.kt | 25 +- .../member/MemberControllerTest.kt | 361 +++++++++--------- 2 files changed, 188 insertions(+), 198 deletions(-) diff --git a/src/main/kotlin/com/petqua/presentation/member/MemberController.kt b/src/main/kotlin/com/petqua/presentation/member/MemberController.kt index b1fdf902..1456769d 100644 --- a/src/main/kotlin/com/petqua/presentation/member/MemberController.kt +++ b/src/main/kotlin/com/petqua/presentation/member/MemberController.kt @@ -6,7 +6,6 @@ import com.petqua.common.config.SIGN_UP_TOKEN_SECURITY_SCHEME_KEY import com.petqua.domain.auth.Auth import com.petqua.domain.auth.LoginMember import com.petqua.domain.auth.SignUpGuest -import com.petqua.presentation.member.dto.MemberAddProfileRequest import com.petqua.presentation.member.dto.MemberSignUpRequest import com.petqua.presentation.member.dto.UpdateProfileRequest import io.swagger.v3.oas.annotations.Operation @@ -71,18 +70,18 @@ class MemberController( return ResponseEntity.noContent().build() } - @Operation(summary = "회원 물생활 프로필 입력 API", description = "회원의 추가적인 물생활 정보를 입력합니다") - @ApiResponse(responseCode = "204", description = "회원 물생활 프로필 입력 성공") - @SecurityRequirement(name = ACCESS_TOKEN_SECURITY_SCHEME_KEY) - @PostMapping("/profiles") - fun addProfile( - @Auth loginMember: LoginMember, - @RequestBody request: MemberAddProfileRequest, - ): ResponseEntity { - val command = request.toCommand(loginMember.memberId) - memberService.addProfile(command) - return ResponseEntity.noContent().build() - } +// @Operation(summary = "회원 물생활 프로필 입력 API", description = "회원의 추가적인 물생활 정보를 입력합니다") +// @ApiResponse(responseCode = "204", description = "회원 물생활 프로필 입력 성공") +// @SecurityRequirement(name = ACCESS_TOKEN_SECURITY_SCHEME_KEY) +// @PostMapping("/profiles") +// fun addProfile( +// @Auth loginMember: LoginMember, +// @RequestBody request: MemberAddProfileRequest, +// ): ResponseEntity { +// val command = request.toCommand(loginMember.memberId) +// memberService.addProfile(command) +// return ResponseEntity.noContent().build() +// } @Operation(summary = "회원 프로필 수정 API", description = "회원의 프로필 정보를 수정합니다") @ApiResponse(responseCode = "204", description = "회원 프로필 수정 성공") diff --git a/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt b/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt index e2d0c82a..04b8c691 100644 --- a/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt +++ b/src/test/kotlin/com/petqua/presentation/member/MemberControllerTest.kt @@ -8,8 +8,6 @@ import com.petqua.domain.fish.FishRepository import com.petqua.domain.member.FishTankRepository import com.petqua.domain.member.MemberRepository import com.petqua.domain.member.PetFishRepository -import com.petqua.domain.member.PetFishSex -import com.petqua.domain.member.TankSize import com.petqua.domain.member.nickname.NicknameWord import com.petqua.domain.member.nickname.NicknameWordRepository import com.petqua.domain.policy.bannedword.BannedWord @@ -18,14 +16,10 @@ import com.petqua.exception.member.MemberExceptionType import com.petqua.exception.member.MemberExceptionType.CONTAINING_BANNED_WORD_NAME import com.petqua.exception.member.MemberExceptionType.HAS_SIGNED_UP_MEMBER import com.petqua.exception.member.MemberExceptionType.NOT_FOUND_MEMBER -import com.petqua.presentation.member.dto.MemberAddProfileRequest import com.petqua.presentation.member.dto.MemberSignUpRequest -import com.petqua.presentation.member.dto.PetFishAddRequest import com.petqua.presentation.member.dto.UpdateProfileRequest import com.petqua.test.ApiTestConfig -import com.petqua.test.fixture.fish import com.petqua.test.fixture.member -import com.petqua.test.fixture.memberAddProfileRequest import io.kotest.assertions.assertSoftly import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -36,10 +30,7 @@ import org.springframework.http.HttpStatus.BAD_REQUEST import org.springframework.http.HttpStatus.CREATED import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.http.HttpStatus.NO_CONTENT -import java.time.LocalDate -import java.time.YearMonth -import java.util.Date -import kotlin.Long.Companion.MIN_VALUE +import java.util.* class MemberControllerTest( private val nicknameWordRepository: NicknameWordRepository, @@ -140,181 +131,181 @@ class MemberControllerTest( } } } - - Given("회원 물생활 프로필을 등록할 때") { - nicknameWordRepository.saveAll( - listOf( - NicknameWord(word = "펫쿠아"), - NicknameWord(word = "물고기"), - ) - ) - bannedWordRepository.saveAll( - listOf( - BannedWord(word = "금지"), - BannedWord(word = "단어") - ) - ) - val fish = fishRepository.save(fish()) - - val accessToken = signInAsMember().accessToken - val memberId = getMemberIdByAccessToken(accessToken) - - When("어항과 수조, 반려어 등 정보를 입력하면") { - val request = MemberAddProfileRequest( - fishTankName = "펫쿠아 어항", - installationDate = YearMonth.of(2024, 3), - fishTankSize = "TANK_1", - fishLifeYear = 1, - petFishes = listOf( - PetFishAddRequest( - fishId = fish.id, - sex = "FEMALE", - count = 1 - ) - ) - ) - - val response = requestAddProfile(request, accessToken) - - Then("회원의 물생활 정보가 저장된다") { - assertSoftly { - response.statusCode shouldBe NO_CONTENT.value() - - val member = memberRepository.findByIdOrThrow(memberId) - member.fishTankCount shouldBe 1 - member.fishLifeYear.value shouldBe 1 - - val fishTank = fishTankRepository.findByMemberId(member.id)[0] - fishTank.memberId shouldBe member.id - fishTank.name.value shouldBe "펫쿠아 어항" - fishTank.installationDate shouldBe LocalDate.of(2024, 3, 1) - fishTank.size shouldBe TankSize.TANK_1 - - val petFish = petFishRepository.findByFishTankId(fishTank.id)[0] - petFish.memberId shouldBe member.id - petFish.fishId shouldBe fish.id - petFish.fishTankId shouldBe fishTank.id - petFish.sex shouldBe PetFishSex.FEMALE - petFish.count.value shouldBe 1 - } - } - } - - When("수조 이름으로 부적절한 단어를 입력하면") { - val request = memberAddProfileRequest(fishTankName = "금지단어포함이름") - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe CONTAINING_BANNED_WORD_NAME.errorMessage() - } - } - } - - When("유효하지 않은 물생활 경력을 입력하면") { - val request = memberAddProfileRequest(fishLifeYear = -1) - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_LIFE_YEAR.errorMessage() - } - } - } - - When("수조 이름 정책을 위반해서 입력하면") { - val request = memberAddProfileRequest(fishTankName = "열 여 덟 자 이 상 으 로 긴 어 항 이 름") - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_TANK_NAME.errorMessage() - } - } - } - - When("존재하지 않는 수조 크기를 입력하면") { - val request = memberAddProfileRequest(fishTankSize = "TANK_100") - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_TANK_SIZE.errorMessage() - } - } - } - - When("존재하지 않는 반려어 id를 입력하면") { - val request = memberAddProfileRequest( - petFishes = listOf( - PetFishAddRequest( - fishId = MIN_VALUE, - sex = "FEMALE", - count = 1 - ) - ) - ) - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_PET_FISH.errorMessage() - } - } - } - - When("존재하지 않는 반려어 성별을 입력하면") { - val request = memberAddProfileRequest( - petFishes = listOf( - PetFishAddRequest( - fishId = MIN_VALUE, - sex = "수컷", - count = 1 - ) - ) - ) - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_SEX.errorMessage() - } - } - } - - When("유효하지 않은 반려어 마릿수를 입력하면") { - val request = memberAddProfileRequest( - petFishes = listOf( - PetFishAddRequest( - fishId = MIN_VALUE, - sex = "FEMALE", - count = -1 - ) - ) - ) - val response = requestAddProfile(request, accessToken) - - Then("예외를 응답한다") { - val errorResponse = response.`as`(ExceptionResponse::class.java) - assertSoftly(response) { - statusCode shouldBe BAD_REQUEST.value() - errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_PET_FISH_COUNT.errorMessage() - } - } - } - } +// +// Given("회원 물생활 프로필을 등록할 때") { +// nicknameWordRepository.saveAll( +// listOf( +// NicknameWord(word = "펫쿠아"), +// NicknameWord(word = "물고기"), +// ) +// ) +// bannedWordRepository.saveAll( +// listOf( +// BannedWord(word = "금지"), +// BannedWord(word = "단어") +// ) +// ) +// val fish = fishRepository.save(fish()) +// +// val accessToken = signInAsMember().accessToken +// val memberId = getMemberIdByAccessToken(accessToken) +// +// When("어항과 수조, 반려어 등 정보를 입력하면") { +// val request = MemberAddProfileRequest( +// fishTankName = "펫쿠아 어항", +// installationDate = YearMonth.of(2024, 3), +// fishTankSize = "TANK_1", +// fishLifeYear = 1, +// petFishes = listOf( +// PetFishAddRequest( +// fishId = fish.id, +// sex = "FEMALE", +// count = 1 +// ) +// ) +// ) +// +// val response = requestAddProfile(request, accessToken) +// +// Then("회원의 물생활 정보가 저장된다") { +// assertSoftly { +// response.statusCode shouldBe NO_CONTENT.value() +// +// val member = memberRepository.findByIdOrThrow(memberId) +// member.fishTankCount shouldBe 1 +// member.fishLifeYear.value shouldBe 1 +// +// val fishTank = fishTankRepository.findByMemberId(member.id)[0] +// fishTank.memberId shouldBe member.id +// fishTank.name.value shouldBe "펫쿠아 어항" +// fishTank.installationDate shouldBe LocalDate.of(2024, 3, 1) +// fishTank.size shouldBe TankSize.TANK_1 +// +// val petFish = petFishRepository.findByFishTankId(fishTank.id)[0] +// petFish.memberId shouldBe member.id +// petFish.fishId shouldBe fish.id +// petFish.fishTankId shouldBe fishTank.id +// petFish.sex shouldBe PetFishSex.FEMALE +// petFish.count.value shouldBe 1 +// } +// } +// } +// +// When("수조 이름으로 부적절한 단어를 입력하면") { +// val request = memberAddProfileRequest(fishTankName = "금지단어포함이름") +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe CONTAINING_BANNED_WORD_NAME.errorMessage() +// } +// } +// } +// +// When("유효하지 않은 물생활 경력을 입력하면") { +// val request = memberAddProfileRequest(fishLifeYear = -1) +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_LIFE_YEAR.errorMessage() +// } +// } +// } +// +// When("수조 이름 정책을 위반해서 입력하면") { +// val request = memberAddProfileRequest(fishTankName = "열 여 덟 자 이 상 으 로 긴 어 항 이 름") +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_TANK_NAME.errorMessage() +// } +// } +// } +// +// When("존재하지 않는 수조 크기를 입력하면") { +// val request = memberAddProfileRequest(fishTankSize = "TANK_100") +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_TANK_SIZE.errorMessage() +// } +// } +// } +// +// When("존재하지 않는 반려어 id를 입력하면") { +// val request = memberAddProfileRequest( +// petFishes = listOf( +// PetFishAddRequest( +// fishId = MIN_VALUE, +// sex = "FEMALE", +// count = 1 +// ) +// ) +// ) +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_PET_FISH.errorMessage() +// } +// } +// } +// +// When("존재하지 않는 반려어 성별을 입력하면") { +// val request = memberAddProfileRequest( +// petFishes = listOf( +// PetFishAddRequest( +// fishId = MIN_VALUE, +// sex = "수컷", +// count = 1 +// ) +// ) +// ) +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_FISH_SEX.errorMessage() +// } +// } +// } +// +// When("유효하지 않은 반려어 마릿수를 입력하면") { +// val request = memberAddProfileRequest( +// petFishes = listOf( +// PetFishAddRequest( +// fishId = MIN_VALUE, +// sex = "FEMALE", +// count = -1 +// ) +// ) +// ) +// val response = requestAddProfile(request, accessToken) +// +// Then("예외를 응답한다") { +// val errorResponse = response.`as`(ExceptionResponse::class.java) +// assertSoftly(response) { +// statusCode shouldBe BAD_REQUEST.value() +// errorResponse.message shouldBe MemberExceptionType.INVALID_MEMBER_PET_FISH_COUNT.errorMessage() +// } +// } +// } +// } Given("회원 프로필을 수정할 때") { val accessToken = signInAsMember().accessToken