Skip to content

Commit

Permalink
feature: 공지사항 조회 (#20)
Browse files Browse the repository at this point in the history
* feat: Announcement 도메인 추가

* feat: Announcement 조회 로직 추가

* feat: Announcement 조회 API 추가

* style: method, dto naming 수정

* test: 공지사항 조회 api test 추가

* refactor: 불필요한 응답 필드 제거

* test: test 패키지 이동

* test: ProductControllerTest에 ApiTestConfig 적용

* refactor: BaseEntity 적용
  • Loading branch information
TaeyeonRoyce authored Jan 27, 2024
1 parent 69cd895 commit 8ab5b18
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.petqua.application.announcement

import com.petqua.domain.announcement.Announcement

data class AnnouncementResponse(
val id: Long,
val title: String,
val linkUrl: String,
) {
companion object {
fun from(announcement: Announcement): AnnouncementResponse {
return AnnouncementResponse(
id = announcement.id,
title = announcement.title,
linkUrl = announcement.linkUrl,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.petqua.application.announcement

import com.petqua.domain.announcement.AnnouncementRepository
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional
@Service
class AnnouncementService(
private val announcementRepository: AnnouncementRepository,
) {

@Cacheable("announcements")
@Transactional(readOnly = true)
fun readAll(): List<AnnouncementResponse> {
val announcements = announcementRepository.findAll()
return announcements.map { AnnouncementResponse.from(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CacheConfiguration {
@Bean
fun cacheManager(): CacheManager {
val cacheManager = ConcurrentMapCacheManager()
cacheManager.setCacheNames(listOf("banners"))
cacheManager.setCacheNames(listOf("banners", "announcements"))
return cacheManager
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/com/petqua/domain/announcement/Announcement.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.petqua.domain.announcement

import com.petqua.common.domain.BaseEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id

@Entity
class Announcement(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,

@Column(nullable = false)
val title: String,

@Column(nullable = false)
val linkUrl: String,
) : BaseEntity()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.petqua.domain.announcement

import org.springframework.data.jpa.repository.JpaRepository

interface AnnouncementRepository : JpaRepository<Announcement, Long>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.petqua.presentation.announcement

import com.petqua.application.announcement.AnnouncementResponse
import com.petqua.application.announcement.AnnouncementService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RequestMapping("/announcements")
@RestController
class AnnouncementController(
private val announcementService: AnnouncementService
) {

@GetMapping
fun readAll(): ResponseEntity<List<AnnouncementResponse>> {
val response = announcementService.readAll()
return ResponseEntity.ok(response)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.petqua.application.announcement

import com.petqua.domain.announcement.Announcement
import com.petqua.domain.announcement.AnnouncementRepository
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import org.mockito.Mockito.atMost
import org.mockito.Mockito.verify
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE
import org.springframework.boot.test.mock.mockito.SpyBean
import org.springframework.test.context.TestConstructor
import org.springframework.test.context.TestConstructor.AutowireMode.ALL

@TestConstructor(autowireMode = ALL)
@SpringBootTest(webEnvironment = NONE)
class AnnouncementServiceTest(
private var announcementService: AnnouncementService,
@SpyBean private var announcementRepository: AnnouncementRepository,
) : BehaviorSpec({

Given("공지사항 조회 테스트") {
announcementRepository.saveAll(
listOf(
Announcement(title = "titleA", linkUrl = "linkUrlA"),
Announcement(title = "titleB", linkUrl = "linkUrlB"),
Announcement(title = "titleC", linkUrl = "linkUrlC"),
)
)

When("공지사항을 전체 조회 하면") {
val results = announcementService.readAll()

Then("모든 공지사항이 조회 된다") {
results.size shouldBe 3
}
}

When("공지사항이 캐싱 되어 있으면") {
repeat(5) { announcementService.readAll() }

Then("퀴리가 발생 하지 않는다") {
verify(announcementRepository, atMost(1)).findAll()
}
}
}
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.petqua.application
package com.petqua.application.product

import com.petqua.application.product.ProductService
import com.petqua.application.product.dto.ProductDetailResponse
import com.petqua.application.product.dto.ProductReadRequest
import com.petqua.application.product.dto.ProductsResponse
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.petqua.presentation.announcement

import com.petqua.application.announcement.AnnouncementResponse
import com.petqua.domain.announcement.Announcement
import com.petqua.domain.announcement.AnnouncementRepository
import com.petqua.test.ApiTestConfig
import io.restassured.module.kotlin.extensions.Extract
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import org.assertj.core.api.SoftAssertions.assertSoftly
import org.springframework.http.HttpStatus

class AnnouncementControllerTest(
private val announcementRepository: AnnouncementRepository
) : ApiTestConfig() {

init {
Given("공지 사항이 존재할 때") {
val announcements = announcementRepository.saveAll(
listOf(
Announcement(title = "announcementsA", linkUrl = "linkUrlA"),
Announcement(title = "announcementsB", linkUrl = "linkUrlB")
)
)

When("공지 사항 목록을 조회하면") {
val response = Given {
log().all()
} When {
get("/announcements")
} Then {
log().all()
} Extract {
response()
}

Then("모든 공지 사항 목록이 반환된다.") {
val responseData = response.`as`(Array<AnnouncementResponse>::class.java)

assertSoftly {
it.assertThat(response.statusCode).isEqualTo(HttpStatus.OK.value())
it.assertThat(responseData.size).isEqualTo(2)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.petqua.presentation
package com.petqua.presentation.product

import com.petqua.application.product.dto.ProductDetailResponse
import com.petqua.application.product.dto.ProductsResponse
Expand All @@ -20,15 +20,15 @@ import io.restassured.module.kotlin.extensions.Extract
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import java.math.BigDecimal
import kotlin.Long.Companion.MIN_VALUE
import org.assertj.core.api.SoftAssertions.assertSoftly
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatus.BAD_REQUEST
import org.springframework.http.HttpStatus.NOT_FOUND
import java.math.BigDecimal
import kotlin.Long.Companion.MIN_VALUE

@SpringBootTest(webEnvironment = RANDOM_PORT)
class ProductControllerTest(
Expand Down
6 changes: 5 additions & 1 deletion src/test/kotlin/com/petqua/test/ApiTestConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ import org.springframework.boot.test.web.server.LocalServerPort
abstract class ApiTestConfig : BehaviorSpec() {

@LocalServerPort
protected val port: Int = RestAssured.port
protected var port: Int = 0

@Autowired
private lateinit var dataCleaner: DataCleaner

init {
this.beforeTest {
RestAssured.port = this.port
}

afterContainer {
dataCleaner.clean()
}
Expand Down

0 comments on commit 8ab5b18

Please sign in to comment.