Skip to content

Commit

Permalink
6/20 배포 작업 (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
cookienc authored Jun 20, 2024
1 parent 9eada97 commit 8afa9d4
Show file tree
Hide file tree
Showing 27 changed files with 1,058 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/db_backup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: db_backup

on:
schedule:
- cron: '0 3 * * *'
- cron: '0 20 * * *'

jobs:
cron:
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ dependencies {

runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'

// RestAssured
testImplementation 'io.rest-assured:rest-assured'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface MacbookRepository: JpaRepository<Macbook, Long>, MacbookRepositoryCust
"""
select m
from Macbook m
join fetch m.prices.macbookPrices
left join fetch m.prices.macbookPrices
where m.category = :category
"""
)
Expand All @@ -30,7 +30,7 @@ interface MacbookRepository: JpaRepository<Macbook, Long>, MacbookRepositoryCust
"""
select m
from Macbook m
join fetch m.prices.macbookPrices
left join fetch m.prices.macbookPrices
where m.id = :id
"""
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MacbookRepositoryImpl(
): List<Macbook> {
return jpaQueryFactory
.selectFrom(macbook)
.join(macbook.prices.macbookPrices, macbookPrice).fetchJoin()
.leftJoin(macbook.prices.macbookPrices, macbookPrice).fetchJoin()
.where(
equalSize(filterCondition.size),
equalColor(filterCondition.color),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,21 @@ class MacbookService(
fun findAllFetchByCategory(
category: MacbookCategory,
): List<Macbook> {
return macbookRepository.findAllFetchByProductCategory(category)
val findAllFetchByProductCategory = macbookRepository.findAllFetchByProductCategory(category)
return findAllFetchByProductCategory
}

@Transactional(readOnly = true)
fun findAllByProductCategoryAndFilter(
fun findAllByCategoryAndFilter(
category: MacbookCategory,
filterCondition: MacbookFilterCondition
): List<Macbook> {
return macbookRepository.findAllByFilterCondition(category, filterCondition)
val findAllByFilterCondition = macbookRepository.findAllByFilterCondition(category, filterCondition)
return findAllByFilterCondition
}

@Transactional(readOnly = true)
fun findAllProductsByFilter(
fun findAllFetchByCategoryAndFilter(
category: MacbookCategory,
macbookFilterCondition: MacbookFilterCondition,
): List<Macbook> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ data class Pages<T>(
) {

companion object {
fun <T> withPagination(data: List<T>) = Pages(
data = data,
pageInfo = PageInfo(currentPage = 1, lastPage = 1, elementSize = data.size)
)
fun <T> withPagination(pagesData: Page<T>) = Pages(
data = pagesData.content,
pageInfo = PageInfo.from(pagesData)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package backend.itracker.tracker.product.controller

import backend.itracker.crawl.common.ProductCategory
import backend.itracker.tracker.common.response.Pages
import backend.itracker.tracker.product.handler.ProductHandlerImpl
import backend.itracker.tracker.product.response.product.CommonProductModel
import backend.itracker.tracker.product.vo.Limit
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
class HomeController(
private val productHandler: ProductHandlerImpl,
) {

@GetMapping("/api/v1/products")
fun getAllProductsByFilter(
@RequestParam(defaultValue = "10") limit: Int
): ResponseEntity<Pages<CommonProductModel>> {
val macbookAirs =
productHandler.findTopDiscountPercentageProducts(ProductCategory.MACBOOK_AIR, Limit(limit))
val macbookPros =
productHandler.findTopDiscountPercentageProducts(ProductCategory.MACBOOK_PRO, Limit(limit))
val airPods =
productHandler.findTopDiscountPercentageProducts(ProductCategory.AIRPODS, Limit(limit))

val allProducts = macbookAirs + macbookPros + airPods
return ResponseEntity.ok(Pages.withPagination(allProducts.sortedBy { it.discountPercentage() }
.take(limit)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ProductController(
) {

@GetMapping("/api/v1/products/{category}")
fun findProductsByCategory(
fun findTopDiscountPercentageProductsByCategory(
@PathVariable category: ProductCategory,
@RequestParam(defaultValue = "5") limit: Int,
): ResponseEntity<Pages<CommonProductModel>> {
Expand All @@ -50,7 +50,7 @@ class ProductController(
}

@GetMapping("/api/v1/products/{category}/search")
fun findFilterdMacbookAir(
fun findFilterdProducts(
@PathVariable category: ProductCategory,
@RequestParam filterCondition: Map<String, String>,
@ModelAttribute pageParams: PageParams,
Expand All @@ -66,7 +66,7 @@ class ProductController(
}

@GetMapping("/api/v1/products/{category}/{productId}")
fun findFilterdMacbookAir(
fun findFilterdProductDetail(
@PathVariable category: ProductCategory,
@PathVariable productId: Long,
): ResponseEntity<CommonProductDetailModel> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ class AirPodsHandler(
productCategory: ProductCategory,
limit: Int
): List<CommonProductModel> {
throw NotSupportedException("AirPodsHandler는 최저가 순위를 지원하지 않습니다.")
val airpods = airPodsService.findAllFetch()
val contents = airpods.map { AirPodsResponse.from(it) }
.sortedBy { it.discountPercentage }
.take(limit)

return contents
}

override fun findFilter(productCategory: ProductCategory, filterCondition: ProductFilter): CommonFilterModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class MacbookHandler(
category: ProductCategory,
filter: ProductFilter
): CommonFilterModel {
val macbooks = macbookService.findAllByProductCategoryAndFilter(category.toMacbookCategory(), MacbookFilterCondition(filter.value))
val macbooks = macbookService.findAllByCategoryAndFilter(category.toMacbookCategory(), MacbookFilterCondition(filter.value))

return MacbookFilterResponse.from(macbooks)
}
Expand All @@ -51,7 +51,7 @@ class MacbookHandler(
filter: ProductFilter,
pageable: Pageable,
): Page<CommonProductModel> {
val macbooks = macbookService.findAllProductsByFilter(
val macbooks = macbookService.findAllFetchByCategoryAndFilter(
category.toMacbookCategory(),
MacbookFilterCondition(filter.value),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import java.time.LocalDateTime
import java.time.Period
import java.time.format.DateTimeFormatter

interface CommonProductModel
interface CommonProductModel {

fun discountPercentage(): Int
}


abstract class CommonProductDetailModel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ data class AirPodsResponse(
)
}
}

override fun discountPercentage(): Int {
return discountPercentage
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,46 @@ class MacbookDetailResponse(
)
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as MacbookDetailResponse

if (id != other.id) return false
if (title != other.title) return false
if (category != other.category) return false
if (size != other.size) return false
if (chip != other.chip) return false
if (cpu != other.cpu) return false
if (gpu != other.gpu) return false
if (storage != other.storage) return false
if (memory != other.memory) return false
if (color != other.color) return false
if (label != other.label) return false
if (imageUrl != other.imageUrl) return false
if (coupangUrl != other.coupangUrl) return false
if (isOutOfStock != other.isOutOfStock) return false

return true
}

override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + category.hashCode()
result = 31 * result + size
result = 31 * result + chip.hashCode()
result = 31 * result + cpu.hashCode()
result = 31 * result + gpu.hashCode()
result = 31 * result + storage.hashCode()
result = 31 * result + memory.hashCode()
result = 31 * result + color.hashCode()
result = 31 * result + label.hashCode()
result = 31 * result + imageUrl.hashCode()
result = 31 * result + coupangUrl.hashCode()
result = 31 * result + isOutOfStock.hashCode()
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ data class MacbookResponse(
storage = "${macbook.storage} SSD 저장 장치",
memory = "${macbook.memory} 통합 메모리",
color = macbook.color,
currentPrice = macbook.findCurrentPrice(),
currentPrice = macbook.findCurrentPrice().setScale(0),
label = macbook.isAllTimeLowPrice(),
imageUrl = macbook.thumbnail,
isOutOfStock = macbook.isOutOfStock()
)
}
}

override fun discountPercentage(): Int {
return discountPercentage
}
}

38 changes: 38 additions & 0 deletions src/test/kotlin/backend/itracker/config/AssuredTestConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package backend.itracker.config

import backend.itracker.tracker.common.PathParams
import backend.itracker.tracker.common.QueryParams
import io.restassured.RestAssured
import org.junit.jupiter.api.BeforeEach
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.http.MediaType


class AssuredTestConfig : ServiceTestConfig() {

@BeforeEach
fun assuredTestSetUp(@LocalServerPort port: Int) {
RestAssured.port = port
}

fun get(url: String) = given()
.`when`().get(url)
.then().log().ifError()
.extract()

fun get(url: String, pathParams: PathParams) = given()
.pathParams(pathParams.values)
.`when`().get(url)
.then().log().ifError()
.extract()

fun get(url: String, pathParams: PathParams, queryParams: QueryParams) = given()
.pathParams(pathParams.values)
.queryParams(queryParams.values)
.`when`().get(url)
.then().log().ifError()
.extract()

private fun given() = RestAssured.given().log().ifValidationFails()
.contentType(MediaType.APPLICATION_JSON_VALUE)
}
36 changes: 36 additions & 0 deletions src/test/kotlin/backend/itracker/config/DatabaseTruncater.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package backend.itracker.config

import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.test.context.TestContext
import org.springframework.test.context.support.AbstractTestExecutionListener

class DatabaseTruncater : AbstractTestExecutionListener() {

override fun afterTestMethod(testContext: TestContext) {
val jdbcTemplate = getJdbcTemplate(testContext)
val truncateQueries = getTruncateQueries(jdbcTemplate)
truncateTables(jdbcTemplate, truncateQueries)
}

private fun getTruncateQueries(jdbcTemplate: JdbcTemplate): List<String> {
return jdbcTemplate.queryForList(
"""
SELECT Concat('TRUNCATE TABLE ', TABLE_NAME, ';') AS q
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'PUBLIC'
""", String::class.java
)
}

private fun getJdbcTemplate(testContext: TestContext): JdbcTemplate {
return testContext.applicationContext.getBean(JdbcTemplate::class.java)
}

private fun truncateTables(jdbcTemplate: JdbcTemplate, truncateQueries: List<String>) {
execute(jdbcTemplate, "SET REFERENTIAL_INTEGRITY FALSE")
truncateQueries.forEach { execute(jdbcTemplate, it) }
execute(jdbcTemplate, "SET REFERENTIAL_INTEGRITY TRUE")
}

private fun execute(jdbcTemplate: JdbcTemplate, query: String) = jdbcTemplate.execute(query)
}
10 changes: 10 additions & 0 deletions src/test/kotlin/backend/itracker/config/RepositoryTestConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package backend.itracker.config

import backend.itracker.crawl.macbook.domain.repository.MacbookRepository
import org.springframework.beans.factory.annotation.Autowired

abstract class RepositoryTestConfig {

@Autowired
lateinit var macbookRepository: MacbookRepository
}
20 changes: 20 additions & 0 deletions src/test/kotlin/backend/itracker/config/ServiceTestConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package backend.itracker.config

import backend.itracker.crawl.macbook.domain.Macbook
import backend.itracker.crawl.macbook.service.MacbookService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.TestExecutionListeners

@TestExecutionListeners(value = [DatabaseTruncater::class], mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class ServiceTestConfig : RepositoryTestConfig() {

@Autowired
lateinit var macbookService: MacbookService

fun saveMacbook(macbook: Macbook) : Macbook {
macbookRepository.save(macbook)
return macbook
}
}
Loading

0 comments on commit 8afa9d4

Please sign in to comment.