Skip to content

Commit

Permalink
[Refactor] 유저의 공개/비공개 필드로 goal 조회 가능 여부 설정 (#75)
Browse files Browse the repository at this point in the history
* feat : Goals Public 여부 필드와 update 메서드 구현 (#69)

* feat : isGoalsPublic update API 구현 (#69)

* chore : getById 한 줄로 표현 (#69)

* chore : isGoalsPulbic이라는 표현을 사용하는 메서드나 클래스에서 is 제거 (#69)

* test : isGoalsPublic update 테스트 코드 작성 (#69)

* feat : 다른 유저의 전체 Goal을 조회하는 API 구현 (#69)

* refactor : 기존의 로그인된 유저 목표 전체 조회 API를 GoalsController로 옮김 (#69)

* refactor : API 이름을 적절하게 변경 (#69)

* refactor : Goal 조회시, Goal 주인의 목표 공개 여부 검증 기능 추가 (#69)

* test : UserServiceTest에서 불필요한 UserRepository 제거 (#69)

* test : 다른 유저의 Goal 조회와 공개/비공개 설정에 따른 결과 테스트 코드 작성 (#69)

* ktlint formatting (#69)

* feat : 유저의 Goals와 Publication을 동시에 쿼리하는 Repository 구현 (#69)

* test : GoalsAndPublicationRepository 테스트 코드 작성 (#69)

* ktlint formatting (#69)

* feat : Goal들의 합과, 공개 여부를 표현하는 LifeMap Entity 구현 (#69)

* refactor : GoalsAndPublication 관련 클래스 전체 삭제.. (#69)

* feat : LifeMap Entity에 Goals 추가 (#69)

* feat : LifeMap 조회 API 구현 - Goals의 정렬과 Public 설정 확인 등을 포함 (#69)

* refactor : 공개 여부 수정 API를 User가 아닌 LifeMap에 구현 및 관련 코드 변경 (#69)

* refactor : 공개 여부 수정 API 이름 변경 (#69)

* refactor : 공개 여부 수정 API 이름 변경 (#69)

* refactor : Goal의 변경에 따른 Task 변경 (#69)

* refactor : LifeMap 전체 조회 메서드를 단건 첫 객체 조회로 변경 (#69)

* refactor : Goal의 필드 변화에 따라 생성 방식 변경 (#69)

* test : Goal 생성 방식 변경에 따른 테스트 코드 변경 (#69)

* chore : LifeMap 관련 클래스 패키지 변경 (#69)

* ktlint formatting (#69)

* refactor : find LifeMap 메서드들에서 Optional 제거 (#69)

* feat : 유저 회원가입시 기본 LifeMap 1개 제공 기능 구현 (#69)

* refactor : 모든 userName 명명 변수 username로 변경 (#69)

* refactor : LifeMap의 Goals 갯수를 세어주는 메서드 구현과 적용 (#69)

* refactor : LifeMap 조회 메서드들의 이름에 First 맥락 추가 (#69)

* test : Repository 메서드 변경에 따른 테스트 코드 변경 (#69)

* test : OAuth2UserService의 upsert method에 대한 테스트 코드 3가지 작성 (#69)

* ktlint main source formatting (#69)
  • Loading branch information
binary-ho authored Jan 11, 2024
1 parent ccf4de9 commit 2698d3b
Show file tree
Hide file tree
Showing 23 changed files with 731 additions and 388 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.raemian.api.auth.controller.request.UpdateUserRequest
import io.raemian.api.auth.controller.response.UserResponse
import io.raemian.api.auth.domain.CurrentUser
import io.raemian.api.auth.service.AuthService
import io.raemian.api.goal.GoalReadService
import io.raemian.api.lifemap.LifeMapService
import io.raemian.api.support.response.ApiResponse
import io.swagger.v3.oas.annotations.Operation
import org.springframework.http.ResponseEntity
Expand All @@ -17,16 +17,15 @@ import org.springframework.web.bind.annotation.RestController
@RestController
class AuthController(
private val authService: AuthService,
private val goalReadService: GoalReadService,
private val lifeMapService: LifeMapService,
) {

@Operation(summary = "토큰 유저 정보 조회 API")
@GetMapping("/my")
fun my(@AuthenticationPrincipal currentUser: CurrentUser): ResponseEntity<ApiResponse<UserResponse>> {
val user = authService.getUserById(currentUser.id)
val goals = goalReadService.findAllByUserId(currentUser.id)
val response = UserResponse.of(user, goals)

val lifeMap = lifeMapService.findFirstByUserId(currentUser.id)
val response = UserResponse.of(user, lifeMap)
return ResponseEntity.ok(ApiResponse.success(response))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.raemian.api.auth.controller.response

import io.raemian.api.auth.domain.UserDTO
import io.raemian.api.goal.controller.response.GoalsResponse
import io.raemian.api.lifemap.controller.LifeMapResponse
import java.time.LocalDate
import java.time.LocalDateTime

Expand All @@ -13,11 +13,10 @@ data class UserResponse(
val birth: LocalDate?,
val image: String,
val createdAt: LocalDateTime?,
val lifeMap: LifeMapSubset,
val goal: GoalSubset,
val lifeMap: LifeMapResponse,
) {
companion object {
fun of(user: UserDTO, goal: GoalsResponse): UserResponse {
fun of(user: UserDTO, lifeMap: LifeMapResponse): UserResponse {
return UserResponse(
id = user.id,
email = user.email,
Expand All @@ -26,18 +25,8 @@ data class UserResponse(
birth = user.birth,
image = user.image,
createdAt = user.createdAt,
goal = GoalSubset(goal.goalsCount),
lifeMap = LifeMapSubset(isPublic = true),
lifeMap = lifeMap,
)
}
}

data class LifeMapSubset(
val isPublic: Boolean = true,
// val id: Long
)

data class GoalSubset(
val count: Int,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.raemian.api.auth.service

import io.raemian.api.auth.domain.CurrentUser
import io.raemian.api.lifemap.LifeMap
import io.raemian.storage.db.core.lifemap.LifeMapRepository
import io.raemian.storage.db.core.user.Authority
import io.raemian.storage.db.core.user.User
import io.raemian.storage.db.core.user.UserRepository
Expand All @@ -14,16 +16,26 @@ import org.springframework.transaction.annotation.Transactional
@Service
class OAuth2UserService(
private val userRepository: UserRepository,
private val lifeMapRepository: LifeMapRepository,
) : DefaultOAuth2UserService() {

companion object {
private const val USERNAME_PREFIX = "BANDIBOODI-"
}

override fun loadUser(userRequest: OAuth2UserRequest): OAuth2User {
val oAuth2User = super.loadUser(userRequest)
val usernameAttributeName = userRequest.clientRegistration
.providerDetails.userInfoEndpoint
.userNameAttributeName

return when (val provider = OAuthProvider.valueOf(userRequest.clientRegistration.registrationId.uppercase())) {
return when (
val provider =
OAuthProvider.valueOf(userRequest.clientRegistration.registrationId.uppercase())
) {
OAuthProvider.GOOGLE -> {
val email = oAuth2User.attributes["email"]?.toString() ?: throw RuntimeException("이메일이없음")
val email =
oAuth2User.attributes["email"]?.toString() ?: throw RuntimeException("이메일이없음")
val name = oAuth2User.attributes["name"]?.toString()
val image = oAuth2User.attributes["picture"]?.toString() ?: ""
val user = upsert(
Expand Down Expand Up @@ -76,25 +88,31 @@ class OAuth2UserService(
}

@Transactional
fun upsert(email: String, image: String, oAuthProvider: OAuthProvider): User {
val user = userRepository.findByEmail(email)
if (user == null) {
val created = userRepository.save(
User(
email = email,
image = image,
provider = oAuthProvider,
authority = Authority.ROLE_USER,
),
)
val new = created.updateUsername("BANDIBOODI-${created.id!!}")
fun upsert(email: String, image: String, oAuthProvider: OAuthProvider): User =
userRepository.findByEmail(email)
?: createUser(email, image, oAuthProvider)

val updated = userRepository.save(new)
private fun createUser(email: String, image: String, oAuthProvider: OAuthProvider): User {
val user = User(
email = email,
image = image,
provider = oAuthProvider,
authority = Authority.ROLE_USER,
)

// lifemap
userRepository.save(user)
val updateUsername = updateUsername(user)
createUserDefaultLifeMap(updateUsername)
return updateUsername
}

return updated
}
return user
private fun updateUsername(user: User): User {
val updateUsername = user.updateUsername("$USERNAME_PREFIX${user.id!!}")
return userRepository.save(updateUsername)
}

private fun createUserDefaultLifeMap(user: User) {
val lifeMap = LifeMap(user, true, goals = ArrayList())
lifeMapRepository.save(lifeMap)
}
}

This file was deleted.

50 changes: 39 additions & 11 deletions application/api/src/main/kotlin/io/raemian/api/goal/GoalService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,42 @@ package io.raemian.api.goal

import io.raemian.api.goal.controller.request.CreateGoalRequest
import io.raemian.api.goal.controller.response.CreateGoalResponse
import io.raemian.api.goal.controller.response.GoalResponse
import io.raemian.api.lifemap.LifeMap
import io.raemian.api.sticker.StickerService
import io.raemian.api.support.RaemianLocalDate
import io.raemian.api.tag.TagService
import io.raemian.api.user.UserService
import io.raemian.storage.db.core.goal.Goal
import io.raemian.storage.db.core.goal.GoalRepository
import io.raemian.storage.db.core.lifemap.LifeMapRepository
import io.raemian.storage.db.core.user.UserRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class GoalService(
private val userService: UserService,
private val stickerService: StickerService,
private val tagService: TagService,
private val userRepository: UserRepository,
private val goalRepository: GoalRepository,
private val lifeMapRepository: LifeMapRepository,
) {

@Transactional(readOnly = true)
fun getById(id: Long): GoalResponse {
val goal = goalRepository.getById(id)
validateLifeMapPublic(goal.lifeMap)
return GoalResponse(goal)
}

@Transactional
fun create(userId: Long, createGoalRequest: CreateGoalRequest): CreateGoalResponse {
val (title, yearOfDeadline, monthOfDeadLine, stickerId, tagId, description) = createGoalRequest

val deadline = RaemianLocalDate.of(yearOfDeadline, monthOfDeadLine)
val sticker = stickerService.getById(stickerId)
val tag = tagService.getById(tagId)
val user = userService.getById(userId)
val lifeMap = lifeMapRepository.findFirstByUserId(userId)
?: createFirstLifeMap(userId)
val goal = createGoal(createGoalRequest, lifeMap)

val goal = Goal(user, title, deadline, sticker, tag, description!!, emptyList())
goalRepository.save(goal)
lifeMap.addGoal(goal)
lifeMapRepository.save(lifeMap)
return CreateGoalResponse(goal)
}

Expand All @@ -39,8 +48,27 @@ class GoalService(
goalRepository.delete(goal)
}

private fun createFirstLifeMap(userId: Long): LifeMap {
val user = userRepository.getById(userId)
return LifeMap(user, true, goals = ArrayList())
}

private fun createGoal(createGoalRequest: CreateGoalRequest, lifeMap: LifeMap): Goal {
val (title, yearOfDeadline, monthOfDeadLine, stickerId, tagId, description) = createGoalRequest
val deadline = RaemianLocalDate.of(yearOfDeadline, monthOfDeadLine)
val sticker = stickerService.getById(stickerId)
val tag = tagService.getById(tagId)
return Goal(lifeMap, title, deadline, sticker, tag, description!!, emptyList())
}

private fun validateLifeMapPublic(lifeMap: LifeMap) {
if (!lifeMap.isPublic) {
throw SecurityException()
}
}

private fun validateGoalIsUsers(userId: Long, goal: Goal) {
if (userId != goal.user.id) {
if (userId != goal.lifeMap.user.id) {
throw SecurityException()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package io.raemian.api.goal.controller

import io.raemian.api.auth.domain.CurrentUser
import io.raemian.api.goal.GoalReadService
import io.raemian.api.goal.GoalService
import io.raemian.api.goal.controller.request.CreateGoalRequest
import io.raemian.api.goal.controller.response.CreateGoalResponse
import io.raemian.api.goal.controller.response.GoalResponse
import io.raemian.api.goal.controller.response.GoalsResponse
import io.raemian.api.support.response.ApiResponse
import io.swagger.v3.oas.annotations.Operation
import org.springframework.http.ResponseEntity
Expand All @@ -26,26 +24,15 @@ fun String.toUri(): URI = URI.create(this)
@RequestMapping("/goal")
class GoalController(
private val goalService: GoalService,
private val goalReadService: GoalReadService,
) {

@Operation(summary = "유저 목표 전체 조회 API")
@GetMapping
fun findAllByUserId(
@AuthenticationPrincipal currentUser: CurrentUser,
): ResponseEntity<ApiResponse<GoalsResponse>> {
val response = goalReadService.findAllByUserId(currentUser.id)
return ResponseEntity
.ok(ApiResponse.success(response))
}

@Operation(summary = "목표 단건 조회 API")
@GetMapping("/{goalId}")
fun getByUserId(
@PathVariable("goalId") goalId: Long,
): ResponseEntity<ApiResponse<GoalResponse>> =
ResponseEntity.ok(
ApiResponse.success(goalReadService.getById(goalId)),
ApiResponse.success(goalService.getById(goalId)),
)

@Operation(summary = "목표 생성 API")
Expand Down

This file was deleted.

Loading

0 comments on commit 2698d3b

Please sign in to comment.