Skip to content

Commit

Permalink
feature: 프로필 수정 API 구현 (#131)
Browse files Browse the repository at this point in the history
* feat: 회원 프로필 수정 컨트롤러 구현

* feat: 회원 프로필 수정 서비스 구현

* test: 회원 프로필 수정 컨트롤러 테스트 작성

* test: 회원 프로필 수정 서비스 테스트 작성

* feat: 닉네임 변경시 중복을 검증하는 로직 변경

* refactor: command가 Nickname객체를 가지게 변경

* chore: 프로필 추가 API 주석 처리
  • Loading branch information
hgo641 authored May 19, 2024
1 parent 92681b6 commit aca4e97
Show file tree
Hide file tree
Showing 9 changed files with 370 additions and 151 deletions.
21 changes: 21 additions & 0 deletions src/main/kotlin/com/petqua/application/member/MemberService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -19,6 +20,7 @@ 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.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
Expand Down Expand Up @@ -88,4 +90,23 @@ class MemberService(
val bannedWords = BannedWords(bannedWordRepository.findAll())
bannedWords.validateContainingBannedWord(name)
}

fun updateProfile(command: UpdateProfileCommand) {
validateNickname(command.nickname, command.memberId)
val member = memberRepository.findActiveByIdOrThrow(command.memberId) {
MemberException(NOT_FOUND_MEMBER)
}
member.updateNickname(command.nickname)
}

private fun validateNickname(nickname: Nickname, memberId: Long) {
validateContainingBannedWord(nickname.value)
validateDuplicatedNickname(nickname, memberId)
}

private fun validateDuplicatedNickname(nickname: Nickname, memberId: Long) {
throwExceptionWhen(memberRepository.existsMemberByNicknameAndIdNot(nickname, memberId)) {
MemberException(ALREADY_EXIST_NICKNAME)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -51,3 +52,11 @@ data class PetFishAddCommand(
)
}
}

data class UpdateProfileCommand(
val memberId: Long,
val nickname: Nickname,
) {

constructor(memberId: Long, nickname: String) : this(memberId, Nickname.from(nickname))
}
2 changes: 2 additions & 0 deletions src/main/kotlin/com/petqua/domain/member/MemberRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ interface MemberRepository : JpaRepository<Member, Long> {

fun existsMemberByNickname(nickname: Nickname): Boolean

fun existsMemberByNicknameAndIdNot(nickname: Nickname, id: Long): Boolean

fun existsMemberByAuthCredentialsId(authCredentialsId: Long): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -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", "서버에서 회원 닉네임을 생성하지 못했습니다.")
Expand Down
28 changes: 21 additions & 7 deletions src/main/kotlin/com/petqua/presentation/member/MemberController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ 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
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.security.SecurityRequirement
Expand All @@ -19,6 +19,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
Expand Down Expand Up @@ -69,16 +70,29 @@ class MemberController(
return ResponseEntity.noContent().build()
}

@Operation(summary = "회원 물생활 프로필 입력 API", description = "회원의 추가적인 물생활 정보를 입력합니다")
@ApiResponse(responseCode = "204", description = "회원 물생활 프로필 입력 성공")
// @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<Unit> {
// 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(
@PatchMapping("/profiles")
fun updateProfile(
@Auth loginMember: LoginMember,
@RequestBody request: MemberAddProfileRequest,
@RequestBody request: UpdateProfileRequest,
): ResponseEntity<Unit> {
val command = request.toCommand(loginMember.memberId)
memberService.addProfile(command)
memberService.updateProfile(command)
return ResponseEntity.noContent().build()
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/com/petqua/presentation/member/dto/MemberDtos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
)
}
}
69 changes: 69 additions & 0 deletions src/test/kotlin/com/petqua/application/member/MemberServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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<MemberException> {
memberService.updateProfile(command)
}.exceptionType() shouldBe CONTAINING_BANNED_WORD_NAME
}
}

When("이미 다른 회원이 사용중인 닉네임이라면") {
memberRepository.save(member(nickname = "변경된 닉네임"))
val command = UpdateProfileCommand(
memberId = member.id,
nickname = "변경된 닉네임",
)

Then("예외를 던진다") {
shouldThrow<MemberException> {
memberService.updateProfile(command)
}.exceptionType() shouldBe ALREADY_EXIST_NICKNAME
}
}

When("회원이 존재하지 않으면") {
val command = UpdateProfileCommand(
memberId = Long.MAX_VALUE,
nickname = "변경된 닉네임",
)

Then("예외를 던진다") {
shouldThrow<MemberException> {
memberService.updateProfile(command)
}.exceptionType() shouldBe NOT_FOUND_MEMBER
}
}
}

afterContainer {
dataCleaner.clean()
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
}
}
Loading

0 comments on commit aca4e97

Please sign in to comment.