Skip to content

Commit

Permalink
feat: 사용자 구독 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
van1164 committed Jun 5, 2024
1 parent bad98dd commit 2d137ed
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.van1164.user.subscribe.controller

import com.van1164.common.security.PrincipalDetails
import com.van1164.user.subscribe.service.SubscribeService
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono


@RestController
@RequestMapping("/api/v1/user/subscribe")
class SubscribeController(
private val subscribeService: SubscribeService
) {

@PostMapping("/{toUserId}")
fun subscribe(
@PathVariable("toUserId") toUserId : String,
@AuthenticationPrincipal principal : Mono<PrincipalDetails>
): Mono<ResponseEntity<String>> {
return principal.flatMap {user->
subscribeService.subscribe(user.name,toUserId)
}
}

@PostMapping("cancel/{toUserId}")
fun subscribeCancel(
@PathVariable("toUserId") toUserId : String,
@AuthenticationPrincipal principal : Mono<PrincipalDetails>
): Mono<ResponseEntity<String>> {
return principal.flatMap {user->
subscribeService.subscribeCancel(user.name,toUserId)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.van1164.user.subscribe.repository

import com.van1164.common.domain.UserSubscribe
import org.springframework.data.r2dbc.repository.R2dbcRepository
import reactor.core.publisher.Mono

interface SubscribeRepository : R2dbcRepository<UserSubscribe,Long> {

fun existsByFromUserIdAndToUserId(fromUserId: String,toUserId: String) : Mono<Boolean>

fun findByFromUserIdAndToUserId(fromUserId : String, toUserId : String) : Mono<UserSubscribe>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.van1164.user.subscribe.service

import com.van1164.common.domain.UserSubscribe
import com.van1164.common.exception.AlreadySubscribeException
import com.van1164.user.UserRepository
import com.van1164.user.subscribe.repository.SubscribeRepository
import org.springframework.data.crossstore.ChangeSetPersister.NotFoundException
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty
import reactor.kotlin.core.publisher.toMono

@Service
class SubscribeService(
private val subscribeRepository: SubscribeRepository,
private val userRepository: UserRepository
) {

@Transactional
fun subscribe(fromUserId: String, toUserId: String): Mono<ResponseEntity<String>> {
return userRepository.findFirstByUserId(fromUserId)
.switchIfEmpty{Mono.error(NotFoundException())}
.flatMap {
userRepository.findFirstByUserId(toUserId)
}
.switchIfEmpty{Mono.error(NotFoundException())}
.flatMap {
subscribeRepository.existsByFromUserIdAndToUserId(fromUserId,toUserId)
}
.filter{it == false}
.switchIfEmpty { Mono.error(AlreadySubscribeException()) }
.map{
UserSubscribe(fromUserId,toUserId)
}
.flatMap {
subscribeRepository.save(it)
}
.map {
ResponseEntity.ok().body("success")
}
.onErrorResume {
when(it){
is NotFoundException -> ResponseEntity.badRequest().body("존재하지 않는 User").toMono()
is AlreadySubscribeException -> ResponseEntity.badRequest().body("이미 구독 중").toMono()
else -> ResponseEntity.badRequest().body("fail").toMono()
}
}
}

@Transactional
fun subscribeCancel(fromUserId: String, toUserId: String): Mono<ResponseEntity<String>> {
return subscribeRepository.findByFromUserIdAndToUserId(fromUserId,toUserId)
.doOnNext {
println(it)
}
.flatMap {
subscribeRepository.delete(it)
}
.thenReturn(ResponseEntity.ok().body("success"))
.defaultIfEmpty(ResponseEntity.badRequest().body("존재하지 않는 User"))
.onErrorReturn(ResponseEntity.badRequest().body("fail"))
}
}
24 changes: 24 additions & 0 deletions video/src/test/kotlin/repository/SubscribeRepositoryTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package repository

import com.van1164.user.subscribe.repository.SubscribeRepository
import com.van1164.video.VideoApplication
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest
import org.springframework.boot.test.context.SpringBootTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

@SpringBootTest(classes = [VideoApplication::class])
class SubscribeRepositoryTest(
@Autowired
private val subscribeRepository: SubscribeRepository
) {

@Test
fun findByFromUserIdAndToUserId(){
val userSubscribe = subscribeRepository.findByFromUserIdAndToUserId("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").block()
assertNotNull(userSubscribe)
assertEquals(userSubscribe.fromUserId,"GOOGLE_van1154van@gmail.com")
}
}
70 changes: 70 additions & 0 deletions video/src/test/kotlin/service/SubscribeServiceTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package service

import com.van1164.user.subscribe.service.SubscribeService
import com.van1164.video.VideoApplication
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.test.annotation.Rollback
import kotlin.test.assertEquals
import kotlin.test.assertNotNull


@SpringBootTest(classes = [VideoApplication::class])
class SubscribeServiceTest @Autowired constructor(
val subscribeService: SubscribeService
) {

@Test
@DisplayName("구독 성공 테스트")
@Rollback(value = true)
fun successTest(){
val subscribe = subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").block()
assert(subscribe != null)
assert(subscribe!!.statusCode == HttpStatus.OK)
assert(subscribe.body != null)
assert(subscribe.body == "success")
}

@Test
@DisplayName("없는 아이디로 인한 구독 실패 테스트")
@Rollback(value = true)
fun noUserIdFailTest(){
val subscribe = subscribeService.subscribe("GOOGLE_van1154van@gmail.com","test@gmail.com").block()
assertNotNull(subscribe)
assertEquals(subscribe.statusCode,HttpStatus.BAD_REQUEST)
assertNotNull(subscribe.body)
assertEquals(subscribe.body ,"존재하지 않는 User")
}

@Test
@DisplayName("이미 구독한 사용자 실패 테스트")
@Rollback(value = true)
fun duplicatedSubscribeFailTest(){
subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").block()
val subscribe = subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").block()
assertNotNull(subscribe)
assertEquals(subscribe.statusCode,HttpStatus.BAD_REQUEST)
assertNotNull(subscribe.body)
assertEquals(subscribe.body ,"이미 구독 중")
}

@Test
@DisplayName("구독 취소 성공 테스트")
@Rollback(value = true)
fun subscribeCancel(){
val subscribe = subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").block()
assertNotNull(subscribe)
assertEquals(subscribe.statusCode,HttpStatus.OK)
assertNotNull(subscribe.body)
assertEquals(subscribe.body ,"success")

val cancel = subscribeService.subscribeCancel("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").block()
assertNotNull(cancel)
assertEquals(cancel.statusCode,HttpStatus.OK)
assertNotNull(cancel.body)
assertEquals(cancel.body ,"success")
}
}

0 comments on commit 2d137ed

Please sign in to comment.