From 6232e86ebee57afb100f110140cefe073f76d9a8 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 00:30:07 +0900 Subject: [PATCH 01/22] =?UTF-8?q?feat=20:=20=EC=8B=A0=EA=B7=9C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EB=93=B1=EB=A1=9D=20=EC=BF=BC=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SpringDataJpaShopPersistenceAdapter.kt | 9 +++++++- .../PersistenceIntegrationTestSupport.kt | 4 ++++ ...SpringDataJpaShopPersistenceAdapterTest.kt | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt index e0955ee..eb52b5f 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapter.kt @@ -4,6 +4,7 @@ import com.mealkitary.common.exception.EntityNotFoundException import com.mealkitary.shop.application.port.output.LoadProductPort import com.mealkitary.shop.application.port.output.LoadReservableTimePort import com.mealkitary.shop.application.port.output.LoadShopPort +import com.mealkitary.shop.application.port.output.SaveShopPort import com.mealkitary.shop.domain.shop.Shop import org.springframework.stereotype.Repository import java.util.Optional @@ -14,7 +15,13 @@ private const val NOT_FOUND_SHOP_MESSAGE = "존재하지 않는 가게입니다. @Repository class SpringDataJpaShopPersistenceAdapter( private val shopRepository: ShopRepository, -) : LoadShopPort, LoadProductPort, LoadReservableTimePort { +) : SaveShopPort, LoadShopPort, LoadProductPort, LoadReservableTimePort { + + override fun saveOne(shop: Shop): Long { + shopRepository.save(shop) + + return shop.id!! + } override fun loadAllShop(): List = shopRepository.findAll() diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt index a1836a0..f6b6d25 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/PersistenceIntegrationTestSupport.kt @@ -3,6 +3,7 @@ package com.mealkitary import com.mealkitary.reservation.application.service.AcceptReservationService import com.mealkitary.reservation.application.service.PayReservationService import com.mealkitary.reservation.application.service.RejectReservationService +import com.mealkitary.shop.application.service.RegisterShopService import com.ninjasquad.springmockk.MockkBean import io.kotest.core.spec.style.AnnotationSpec import io.kotest.extensions.spring.SpringExtension @@ -32,4 +33,7 @@ abstract class PersistenceIntegrationTestSupport : AnnotationSpec() { @MockkBean private lateinit var rejectReservationService: RejectReservationService + + @MockkBean + private lateinit var registerShopService: RegisterShopService } diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt index 17e2bfc..a821b04 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt @@ -3,6 +3,8 @@ package com.mealkitary.shop.persistence import com.mealkitary.PersistenceIntegrationTestSupport import com.mealkitary.common.exception.EntityNotFoundException import com.mealkitary.common.model.Money +import com.mealkitary.shop.domain.shop.Shop +import data.ShopTestData import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldBeEmpty @@ -14,6 +16,25 @@ class SpringDataJpaShopPersistenceAdapterTest( private val adapterUnderTest: SpringDataJpaShopPersistenceAdapter, ) : PersistenceIntegrationTestSupport() { + @Test + fun `db integration test - 신규 가게를 등록한다`() { + em.createQuery("delete from Product p") + .executeUpdate() + em.createNativeQuery("delete from reservable_time") + .executeUpdate() + em.createQuery("delete from Shop s") + .executeUpdate() + + val shop = ShopTestData.defaultShop().build() + val saved = adapterUnderTest.saveOne(shop) + em.flush() + em.clear() + + val find = em.find(Shop::class.java, saved) + + saved shouldBe find.id + } + @Test fun `db integration test - 모든 가게를 조회한다`() { val shops = adapterUnderTest.loadAllShop() From c89b293e0dc29ea6238448a3b9f91a247a5cbdfd Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 00:41:31 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat=20:=20brn=20validator=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C=20=EB=AA=A8=EB=93=88=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter-open-api-brn-validator/build.gradle.kts | 3 +++ .../adapter-simple-brn-validator/build.gradle.kts | 3 +++ settings.gradle.kts | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 mealkitary-infrastructure/business-registration-number-validator/adapter-open-api-brn-validator/build.gradle.kts create mode 100644 mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build.gradle.kts diff --git a/mealkitary-infrastructure/business-registration-number-validator/adapter-open-api-brn-validator/build.gradle.kts b/mealkitary-infrastructure/business-registration-number-validator/adapter-open-api-brn-validator/build.gradle.kts new file mode 100644 index 0000000..c685d19 --- /dev/null +++ b/mealkitary-infrastructure/business-registration-number-validator/adapter-open-api-brn-validator/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + implementation(project(":mealkitary-domain")) +} diff --git a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build.gradle.kts b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build.gradle.kts new file mode 100644 index 0000000..c685d19 --- /dev/null +++ b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + implementation(project(":mealkitary-domain")) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index c8c34b2..52db22b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,7 +6,9 @@ include( "mealkitary-domain", "mealkitary-infrastructure:adapter-persistence-spring-data-jpa", "mealkitary-infrastructure:adapter-paymentgateway-tosspayments", - "mealkitary-infrastructure:adapter-firebase-notification" + "mealkitary-infrastructure:adapter-firebase-notification", + "mealkitary-infrastructure:business-registration-number-validator:adapter-open-api-brn-validator", + "mealkitary-infrastructure:business-registration-number-validator:adapter-simple-brn-validator" ) pluginManagement { @@ -26,6 +28,7 @@ pluginManagement { "org.jetbrains.kotlin.plugin.spring", "org.jetbrains.kotlin.kapt", "org.jetbrains.kotlin.plugin.jpa" -> useVersion(kotlinVersion) + "org.jlleitschuh.gradle.ktlint" -> useVersion(ktlintVersion) "org.asciidoctor.jvm.convert" -> useVersion(asciidoctorVersion) "com.google.cloud.tools.jib" -> useVersion(jibVersion) From 8cf777efb792adce3599bb8f529c8813d02c013f Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 00:47:23 +0900 Subject: [PATCH 03/22] =?UTF-8?q?feat=20:=20=EC=8B=AC=ED=94=8C=20BRN=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/mealkitary/brn/SimpleBrnValidator.kt | 12 ++++++++++++ .../com/mealkitary/brn/SimpleBrnValidatorTest.kt | 13 +++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt create mode 100644 mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt diff --git a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt new file mode 100644 index 0000000..e07fc55 --- /dev/null +++ b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt @@ -0,0 +1,12 @@ +package com.mealkitary.brn + +import com.mealkitary.shop.domain.shop.ShopBusinessNumberValidator +import org.springframework.context.annotation.Primary +import org.springframework.stereotype.Component + +@Component +@Primary +class SimpleBrnValidator : ShopBusinessNumberValidator { + + override fun validate(brn: String) {} +} diff --git a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt new file mode 100644 index 0000000..87f137d --- /dev/null +++ b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt @@ -0,0 +1,13 @@ +package com.mealkitary.brn + +import io.kotest.core.spec.style.AnnotationSpec + +class SimpleBrnValidatorTest : AnnotationSpec() { + + @Test + fun `adapter unit test - 심플 BRN 검사기는 모든 BRN을 통과 시킨다`() { + val adapterUnderTest = SimpleBrnValidator() + + adapterUnderTest.validate("any") + } +} From f060780c878fa048d8547adb10e34abea763e6b5 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 00:51:13 +0900 Subject: [PATCH 04/22] =?UTF-8?q?chore=20:=20brn=20=EA=B2=80=EC=82=AC?= =?UTF-8?q?=EA=B8=B0=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mealkitary-api/build.gradle.kts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mealkitary-api/build.gradle.kts b/mealkitary-api/build.gradle.kts index a6459f9..5ffe9a6 100644 --- a/mealkitary-api/build.gradle.kts +++ b/mealkitary-api/build.gradle.kts @@ -21,6 +21,16 @@ dependencies { implementation(project(":mealkitary-infrastructure:adapter-persistence-spring-data-jpa")) implementation(project(":mealkitary-infrastructure:adapter-paymentgateway-tosspayments")) implementation(project(":mealkitary-infrastructure:adapter-firebase-notification")) + implementation( + project( + ":mealkitary-infrastructure:business-registration-number-validator:adapter-open-api-brn-validator", + ) + ) + implementation( + project( + ":mealkitary-infrastructure:business-registration-number-validator:adapter-simple-brn-validator", + ) + ) testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc") asciidoctorExt("org.springframework.restdocs:spring-restdocs-asciidoctor") } From 40fdefb037fb0777e8cac7364e201f68f3694813 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 02:04:00 +0900 Subject: [PATCH 05/22] =?UTF-8?q?feat=20:=20ShopBusinessNumber=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mealkitary/shop/domain/shop/Shop.kt | 4 ++ .../shop/domain/shop/ShopBusinessNumber.kt | 37 +++++++++++++++++++ .../domain/shop/ShopBusinessNumberTest.kt | 35 ++++++++++++++++++ .../testFixtures/kotlin/data/ShopTestData.kt | 10 ++++- .../com/mealkitary/brn/SimpleBrnValidator.kt | 5 ++- .../mealkitary/brn/SimpleBrnValidatorTest.kt | 3 +- 6 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt create mode 100644 mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt index 8cd2fa6..2bcbbed 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt @@ -22,6 +22,7 @@ import javax.persistence.Table class Shop( title: String, status: ShopStatus, + businessNumber: ShopBusinessNumber, reservableTimes: MutableList, products: MutableList ) { @@ -59,6 +60,9 @@ class Shop( var products: MutableList = products protected set + var businessNumber: ShopBusinessNumber = businessNumber + protected set + private fun checkShopName(title: String) { if (title.isBlank()) { throw IllegalArgumentException("가게 이름을 입력해주세요.") diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt new file mode 100644 index 0000000..bdb0e08 --- /dev/null +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt @@ -0,0 +1,37 @@ +package com.mealkitary.shop.domain.shop + +import javax.persistence.Column +import javax.persistence.Embeddable + +// TODO : 매번 REGEX 객체를 생성하는 것이 성능에 얼마나 영향을 미치는지 확인하고 개선 +private const val BRN_FORMAT = "([0-9]{3})-?([0-9]{2})-?([0-9]{5})" +private const val INVALID_BRN_FORMAT_MESSAGE = "올바른 사업자번호 형식이 아닙니다." + +@Embeddable +class ShopBusinessNumber private constructor( + @Column(name = "brn", nullable = false) + val value: String +) { + companion object { + fun from(brn: String): ShopBusinessNumber { + checkIsBrnBlank(brn) + checkIsBrnFormat(brn) + + return ShopBusinessNumber(brn) + } + + private fun checkIsBrnBlank(brn: String) { + if (brn.isBlank()) { + throw IllegalArgumentException(INVALID_BRN_FORMAT_MESSAGE) + } + } + + private fun checkIsBrnFormat(brn: String) { + if (!brn.matches(getBRNFormatRegex())) { + throw IllegalArgumentException(INVALID_BRN_FORMAT_MESSAGE) + } + } + + private fun getBRNFormatRegex() = Regex(BRN_FORMAT) + } +} diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt new file mode 100644 index 0000000..eb33b30 --- /dev/null +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt @@ -0,0 +1,35 @@ +package com.mealkitary.shop.domain.shop + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage + +class ShopBusinessNumberTest : AnnotationSpec() { + + @Test + fun `사업자 번호는 빈 문자열이 될 수 없다`() { + listOf(" ", " ", "").forAll { + shouldThrow { + ShopBusinessNumber.from(it) + } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." + } + } + + @Test + fun `사업자 번호의 형식이 아니면 예외를 발생한다`() { + listOf("010-1234-5678 ", "inv-al-lidbn", " 123-23-12345 ").forAll { + shouldThrow { + ShopBusinessNumber.from(it) + } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." + } + } + + @Test + fun `올바른 사업자번호 형식을 입력하는 경우에만 객체를 생성할 수 있다`() { + val shopBusinessNumber = ShopBusinessNumber.from("132-32-32112") + + shopBusinessNumber.value shouldBe "132-32-32112" + } +} diff --git a/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt b/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt index ff83349..9d58bf8 100644 --- a/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt +++ b/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt @@ -2,6 +2,7 @@ package data import com.mealkitary.shop.domain.product.Product import com.mealkitary.shop.domain.shop.Shop +import com.mealkitary.shop.domain.shop.ShopBusinessNumber import com.mealkitary.shop.domain.shop.ShopStatus import data.ProductTestData.Companion.defaultProduct import java.time.LocalTime @@ -19,7 +20,8 @@ class ShopTestData { private var products: List = listOf( defaultProduct().withId(1L).withName("부대찌개").build(), defaultProduct().withId(2L).withName("닭볶음탕").build() - ) + ), + private var shopBusinessNumber: ShopBusinessNumber = ShopBusinessNumber.from("123-45-67890") ) { fun withTitle(title: String): ShopBuilder { @@ -42,10 +44,16 @@ class ShopTestData { return this } + fun withBusinessNumber(shopBusinessNumber: ShopBusinessNumber): ShopBuilder { + this.shopBusinessNumber = shopBusinessNumber + return this + } + fun build(): Shop { return Shop( this.title, this.shopStatus, + this.shopBusinessNumber, this.reservableTimes.toMutableList(), this.products.toMutableList() ) diff --git a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt index e07fc55..8010671 100644 --- a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt +++ b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/main/kotlin/com/mealkitary/brn/SimpleBrnValidator.kt @@ -1,6 +1,7 @@ package com.mealkitary.brn -import com.mealkitary.shop.domain.shop.ShopBusinessNumberValidator +import com.mealkitary.shop.domain.shop.ShopBusinessNumber +import com.mealkitary.shop.domain.shop.factory.ShopBusinessNumberValidator import org.springframework.context.annotation.Primary import org.springframework.stereotype.Component @@ -8,5 +9,5 @@ import org.springframework.stereotype.Component @Primary class SimpleBrnValidator : ShopBusinessNumberValidator { - override fun validate(brn: String) {} + override fun validate(brn: ShopBusinessNumber) {} } diff --git a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt index 87f137d..ee18484 100644 --- a/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt +++ b/mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/src/test/kotlin/com/mealkitary/brn/SimpleBrnValidatorTest.kt @@ -1,5 +1,6 @@ package com.mealkitary.brn +import com.mealkitary.shop.domain.shop.ShopBusinessNumber import io.kotest.core.spec.style.AnnotationSpec class SimpleBrnValidatorTest : AnnotationSpec() { @@ -8,6 +9,6 @@ class SimpleBrnValidatorTest : AnnotationSpec() { fun `adapter unit test - 심플 BRN 검사기는 모든 BRN을 통과 시킨다`() { val adapterUnderTest = SimpleBrnValidator() - adapterUnderTest.validate("any") + adapterUnderTest.validate(ShopBusinessNumber.from("123-45-67890")) } } From b449851c7c490cd0365150352ea9ce932fea42b3 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 02:55:01 +0900 Subject: [PATCH 06/22] =?UTF-8?q?feat=20:=20ShopTitle=20=EB=AA=A8=EB=8D=B8?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../factory/ShopBusinessNumberValidator.kt | 8 ++++ .../shop/domain/shop/factory/ShopFactory.kt | 27 +++++++++++++ .../domain/shop/factory/ShopFactoryTest.kt | 40 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopBusinessNumberValidator.kt create mode 100644 mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactory.kt create mode 100644 mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactoryTest.kt diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopBusinessNumberValidator.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopBusinessNumberValidator.kt new file mode 100644 index 0000000..1ce8208 --- /dev/null +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopBusinessNumberValidator.kt @@ -0,0 +1,8 @@ +package com.mealkitary.shop.domain.shop.factory + +import com.mealkitary.shop.domain.shop.ShopBusinessNumber + +interface ShopBusinessNumberValidator { + + fun validate(brn: ShopBusinessNumber) +} diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactory.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactory.kt new file mode 100644 index 0000000..76cc4df --- /dev/null +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactory.kt @@ -0,0 +1,27 @@ +package com.mealkitary.shop.domain.shop.factory + +import com.mealkitary.shop.domain.product.Product +import com.mealkitary.shop.domain.shop.Shop +import com.mealkitary.shop.domain.shop.ShopBusinessNumber +import com.mealkitary.shop.domain.shop.ShopStatus +import com.mealkitary.shop.domain.shop.ShopTitle +import java.time.LocalTime + +class ShopFactory( + private val shopBusinessNumberValidator: ShopBusinessNumberValidator +) { + + fun createOne(title: String, brn: String): Shop { + val shopBusinessNumber = ShopBusinessNumber.from(brn) + + shopBusinessNumberValidator.validate(shopBusinessNumber) + + return Shop( + ShopTitle.from(title), + ShopStatus.VALID, + shopBusinessNumber, + emptyList().toMutableList(), + emptyList().toMutableList(), + ) + } +} diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactoryTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactoryTest.kt new file mode 100644 index 0000000..34a8382 --- /dev/null +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/factory/ShopFactoryTest.kt @@ -0,0 +1,40 @@ +package com.mealkitary.shop.domain.shop.factory + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage +import io.mockk.every +import io.mockk.mockk + +class ShopFactoryTest : AnnotationSpec() { + + private val shopBusinessNumberValidator = mockk() + private val shopFactory = ShopFactory(shopBusinessNumberValidator) + + @Test + fun `사업자번호가 유효하지 않으면 예외를 발생한다`() { + shouldThrow { + shopFactory.createOne("집밥뚝딱 안양점", "32-12-3221") + } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." + } + + @Test + fun `실제로 유효한 사업자번호와 가게이름이라면 가게를 생성한다`() { + every { shopBusinessNumberValidator.validate(any()) } answers { } + + val shop = shopFactory.createOne("집밥뚝딱 안양점", "321-23-12345") + + shop.title.value shouldBe "집밥뚝딱 안양점" + shop.businessNumber.value shouldBe "321-23-12345" + } + + @Test + fun `가게 이름이 유효하지 않으면 예외를 발생한다`() { + every { shopBusinessNumberValidator.validate(any()) } answers { } + + shouldThrow { + shopFactory.createOne("집밥뚝딱 ! 안양점", "321-23-12345") + } shouldHaveMessage "올바른 가게 이름 형식이 아닙니다.(한글, 영문, 공백, 숫자만 포함 가능)" + } +} From fbbb65d9f7b1a7cce03888a60fa8a64423909fac Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 03:00:08 +0900 Subject: [PATCH 07/22] =?UTF-8?q?refactor=20:=20Shop=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=EC=84=9C=20ShopTitle=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/GetShopService.kt | 2 +- .../application/service/GetShopServiceTest.kt | 12 +++- .../com/mealkitary/shop/domain/shop/Shop.kt | 14 +---- .../mealkitary/shop/domain/shop/ShopTitle.kt | 43 ++++++++++++++ .../mealkitary/shop/domain/shop/ShopTest.kt | 19 ------- .../shop/domain/shop/ShopTitleTest.kt | 57 +++++++++++++++++++ .../testFixtures/kotlin/data/ShopTestData.kt | 3 +- ...ingDataJpaReservationPersistenceAdapter.kt | 2 +- 8 files changed, 117 insertions(+), 35 deletions(-) create mode 100644 mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt create mode 100644 mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTitleTest.kt diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/GetShopService.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/GetShopService.kt index 524c05f..e1b3d4b 100644 --- a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/GetShopService.kt +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/GetShopService.kt @@ -12,5 +12,5 @@ class GetShopService( private val loadShopPort: LoadShopPort ) : GetShopQuery { - override fun loadAllShop() = loadShopPort.loadAllShop().map { ShopResponse(it.id, it.title) } + override fun loadAllShop() = loadShopPort.loadAllShop().map { ShopResponse(it.id, it.title.value) } } diff --git a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/GetShopServiceTest.kt b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/GetShopServiceTest.kt index 6aa9185..23b9d34 100644 --- a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/GetShopServiceTest.kt +++ b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/GetShopServiceTest.kt @@ -3,7 +3,9 @@ package com.mealkitary.shop.application.service import com.mealkitary.shop.application.port.input.ShopResponse import com.mealkitary.shop.application.port.output.LoadShopPort import com.mealkitary.shop.domain.shop.Shop +import com.mealkitary.shop.domain.shop.ShopBusinessNumber import com.mealkitary.shop.domain.shop.ShopStatus +import com.mealkitary.shop.domain.shop.ShopTitle import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.shouldBe import io.mockk.every @@ -17,7 +19,15 @@ class GetShopServiceTest : AnnotationSpec() { @Test fun `service unit test - 모든 가게를 조회한다`() { every { loadShopPort.loadAllShop() } answers { - listOf(Shop("집밥뚝딱", ShopStatus.VALID, mutableListOf(), mutableListOf())) + listOf( + Shop( + ShopTitle.from("집밥뚝딱"), + ShopStatus.VALID, + ShopBusinessNumber.from("123-45-67890"), + mutableListOf(), + mutableListOf() + ) + ) } val expected = ShopResponse(null, "집밥뚝딱") diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt index 2bcbbed..e04058c 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt @@ -20,17 +20,13 @@ import javax.persistence.Table @Entity @Table(name = "shop") class Shop( - title: String, + title: ShopTitle, status: ShopStatus, businessNumber: ShopBusinessNumber, reservableTimes: MutableList, products: MutableList ) { - init { - checkShopName(title) - } - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "shop_id") @@ -38,7 +34,7 @@ class Shop( protected set @Column(nullable = false) - var title: String = title + var title: ShopTitle = title protected set @Enumerated(EnumType.STRING) @@ -63,12 +59,6 @@ class Shop( var businessNumber: ShopBusinessNumber = businessNumber protected set - private fun checkShopName(title: String) { - if (title.isBlank()) { - throw IllegalArgumentException("가게 이름을 입력해주세요.") - } - } - fun checkReservableShop() { if (status.isInvalidStatus()) { throw IllegalStateException("유효하지 않은 가게입니다.") diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt new file mode 100644 index 0000000..f8a34f9 --- /dev/null +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt @@ -0,0 +1,43 @@ +package com.mealkitary.shop.domain.shop + +import javax.persistence.Column +import javax.persistence.Embeddable + +private const val MAX_TITLE_LENGTH = 50 +private const val TITLE_FORMAT = "^[a-zA-Z0-9가-힣\\s]*$" + +@Embeddable +class ShopTitle( + @Column(name = "title", nullable = false) + val value: String +) { + companion object { + fun from(title: String): ShopTitle { + checkIsTitleFormat(title) + checkIsTitleBlank(title) + checkTitleLength(title) + + return ShopTitle(title) + } + + private fun checkIsTitleFormat(title: String) { + if (!title.matches(getTitleFormatRegex())) { + throw IllegalArgumentException("올바른 가게 이름 형식이 아닙니다.(한글, 영문, 공백, 숫자만 포함 가능)") + } + } + + private fun checkTitleLength(title: String) { + if (title.length > MAX_TITLE_LENGTH) { + throw IllegalArgumentException("가게의 이름은 최대 50글자입니다.") + } + } + + private fun checkIsTitleBlank(title: String) { + if (title.isBlank()) { + throw IllegalArgumentException("가게 이름을 입력해주세요.") + } + } + + private fun getTitleFormatRegex() = Regex(TITLE_FORMAT) + } +} diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt index d12bf3b..c28d0d9 100644 --- a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTest.kt @@ -4,7 +4,6 @@ import data.ProductTestData.Companion.defaultProduct import data.ShopTestData.Companion.defaultShop import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.AnnotationSpec -import io.kotest.inspectors.forAll import io.kotest.matchers.throwable.shouldHaveMessage class ShopTest : AnnotationSpec() { @@ -61,24 +60,6 @@ class ShopTest : AnnotationSpec() { sut.checkItem(product) } - @Test - fun `가게 이름이 빈 문자열일 경우 예외를 발생한다`() { - shouldThrow { - defaultShop().withTitle("").build() - } shouldHaveMessage "가게 이름을 입력해주세요." - } - - @Test - fun `가게 이름이 공백으로만 이루어져 있을 경우, 예외를 발생한다`() { - val params = listOf(" ", " ", " ", "\n", "\t") - - params.forAll { - shouldThrow { - defaultShop().withTitle(it).build() - } shouldHaveMessage "가게 이름을 입력해주세요." - } - } - private fun productWithIdAndName(id: Long, name: String) = defaultProduct() .withId(id) .withName(name) diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTitleTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTitleTest.kt new file mode 100644 index 0000000..32f2f9c --- /dev/null +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopTitleTest.kt @@ -0,0 +1,57 @@ +package com.mealkitary.shop.domain.shop + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage + +class ShopTitleTest : AnnotationSpec() { + + @Test + fun `가게 이름이 빈 문자열일 경우 예외를 발생한다`() { + shouldThrow { + ShopTitle.from("") + } shouldHaveMessage "가게 이름을 입력해주세요." + } + + @Test + fun `가게 이름이 공백으로만 이루어져 있을 경우, 예외를 발생한다`() { + val params = listOf(" ", " ", " ", "\n", "\t") + + params.forAll { + shouldThrow { + ShopTitle.from(it) + } shouldHaveMessage "가게 이름을 입력해주세요." + } + } + + @Test + fun `가게의 이름은 최대 50글자이다`() { + val title = "가".repeat(51) + + shouldThrow { + ShopTitle.from(title) + } shouldHaveMessage "가게의 이름은 최대 50글자입니다." + } + + @Test + fun `가게의 이름이 올바른 형식이아니라면 예외를 발생한다`() { + listOf( + "!@#집밥뚝딱,", + " 집밥 @ 뚝딱", + "집밥뚝@#@!" + ).forAll { + shouldThrow { + ShopTitle.from(it) + } shouldHaveMessage "올바른 가게 이름 형식이 아닙니다.(한글, 영문, 공백, 숫자만 포함 가능)" + } + } + + @Test + fun `가게 이름의 조건을 모두 만족하면 가게 이름 객체를 반환한다`() { + val shopTitle = ShopTitle.from("집밥뚝딱 안양점") + + shopTitle.value shouldBe "집밥뚝딱 안양점" + } +} diff --git a/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt b/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt index 9d58bf8..1389d2a 100644 --- a/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt +++ b/mealkitary-domain/src/testFixtures/kotlin/data/ShopTestData.kt @@ -4,6 +4,7 @@ import com.mealkitary.shop.domain.product.Product import com.mealkitary.shop.domain.shop.Shop import com.mealkitary.shop.domain.shop.ShopBusinessNumber import com.mealkitary.shop.domain.shop.ShopStatus +import com.mealkitary.shop.domain.shop.ShopTitle import data.ProductTestData.Companion.defaultProduct import java.time.LocalTime @@ -51,7 +52,7 @@ class ShopTestData { fun build(): Shop { return Shop( - this.title, + ShopTitle.from(this.title), this.shopStatus, this.shopBusinessNumber, this.reservableTimes.toMutableList(), diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/SpringDataJpaReservationPersistenceAdapter.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/SpringDataJpaReservationPersistenceAdapter.kt index 009fad5..c3a1a27 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/SpringDataJpaReservationPersistenceAdapter.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/kotlin/com/mealkitary/reservation/persistence/SpringDataJpaReservationPersistenceAdapter.kt @@ -59,7 +59,7 @@ class SpringDataJpaReservationPersistenceAdapter( private fun mapToReservationResponse(reservation: Reservation) = ReservationResponse( reservation.id, - reservation.shop.title, + reservation.shop.title.value, reservation.buildDescription(), reservation.reserveAt, reservation.reservationStatus.name, From b66cfb902c7da69b18ade051c9a47bc20b36449a Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 03:00:36 +0900 Subject: [PATCH 08/22] =?UTF-8?q?style=20:=20lint=20=ED=8F=AC=EB=A7=A4?= =?UTF-8?q?=ED=8C=85=20=EC=88=98=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt | 1 + .../com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt index bdb0e08..9d83760 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt @@ -12,6 +12,7 @@ class ShopBusinessNumber private constructor( @Column(name = "brn", nullable = false) val value: String ) { + companion object { fun from(brn: String): ShopBusinessNumber { checkIsBrnBlank(brn) diff --git a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt index eb33b30..d0f5288 100644 --- a/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt +++ b/mealkitary-domain/src/test/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumberTest.kt @@ -13,7 +13,7 @@ class ShopBusinessNumberTest : AnnotationSpec() { listOf(" ", " ", "").forAll { shouldThrow { ShopBusinessNumber.from(it) - } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." + } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." } } @@ -22,7 +22,7 @@ class ShopBusinessNumberTest : AnnotationSpec() { listOf("010-1234-5678 ", "inv-al-lidbn", " 123-23-12345 ").forAll { shouldThrow { ShopBusinessNumber.from(it) - } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." + } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." } } From 27e3cf3549fc081655abbd340713d300061522c9 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 04:35:24 +0900 Subject: [PATCH 09/22] =?UTF-8?q?feat=20:=20=EC=8B=A0=EA=B7=9C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EB=93=B1=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../port/input/RegisterShopRequest.kt | 6 ++ .../port/input/RegisterShopUseCase.kt | 6 ++ .../application/port/output/SaveShopPort.kt | 8 +++ .../service/RegisterShopService.kt | 26 ++++++++ .../service/RegisterShopServiceTest.kt | 60 +++++++++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopRequest.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopUseCase.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/SaveShopPort.kt create mode 100644 mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/RegisterShopService.kt create mode 100644 mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/RegisterShopServiceTest.kt diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopRequest.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopRequest.kt new file mode 100644 index 0000000..b11e2f1 --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopRequest.kt @@ -0,0 +1,6 @@ +package com.mealkitary.shop.application.port.input + +data class RegisterShopRequest( + val title: String, + val brn: String +) diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopUseCase.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopUseCase.kt new file mode 100644 index 0000000..fcc5ba8 --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/input/RegisterShopUseCase.kt @@ -0,0 +1,6 @@ +package com.mealkitary.shop.application.port.input + +interface RegisterShopUseCase { + + fun register(registerShopRequest: RegisterShopRequest): Long +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/SaveShopPort.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/SaveShopPort.kt new file mode 100644 index 0000000..eda5b38 --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/port/output/SaveShopPort.kt @@ -0,0 +1,8 @@ +package com.mealkitary.shop.application.port.output + +import com.mealkitary.shop.domain.shop.Shop + +interface SaveShopPort { + + fun saveOne(shop: Shop): Long +} diff --git a/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/RegisterShopService.kt b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/RegisterShopService.kt new file mode 100644 index 0000000..8da3cab --- /dev/null +++ b/mealkitary-application/src/main/kotlin/com/mealkitary/shop/application/service/RegisterShopService.kt @@ -0,0 +1,26 @@ +package com.mealkitary.shop.application.service + +import com.mealkitary.shop.application.port.input.RegisterShopRequest +import com.mealkitary.shop.application.port.input.RegisterShopUseCase +import com.mealkitary.shop.application.port.output.SaveShopPort +import com.mealkitary.shop.domain.shop.factory.ShopBusinessNumberValidator +import com.mealkitary.shop.domain.shop.factory.ShopFactory +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional(readOnly = true) +class RegisterShopService( + private val saveShopPort: SaveShopPort, + shopBusinessNumberValidator: ShopBusinessNumberValidator +) : RegisterShopUseCase { + + private val shopFactory = ShopFactory(shopBusinessNumberValidator) + + @Transactional + override fun register(registerShopRequest: RegisterShopRequest): Long { + val shop = shopFactory.createOne(registerShopRequest.title, registerShopRequest.brn) + + return saveShopPort.saveOne(shop) + } +} diff --git a/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/RegisterShopServiceTest.kt b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/RegisterShopServiceTest.kt new file mode 100644 index 0000000..2e2e6be --- /dev/null +++ b/mealkitary-application/src/test/kotlin/com/mealkitary/shop/application/service/RegisterShopServiceTest.kt @@ -0,0 +1,60 @@ +package com.mealkitary.shop.application.service + +import com.mealkitary.shop.application.port.input.RegisterShopRequest +import com.mealkitary.shop.application.port.output.SaveShopPort +import com.mealkitary.shop.domain.shop.Shop +import com.mealkitary.shop.domain.shop.factory.ShopBusinessNumberValidator +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot + +class RegisterShopServiceTest : AnnotationSpec() { + + private val saveShopPort = mockk() + private val shopBusinessNumberValidator = mockk() + private val registerShopService = RegisterShopService(saveShopPort, shopBusinessNumberValidator) + + @Test + fun `service unit test - 신규 가게를 등록한다`() { + val shopSlot = slot() + val request = RegisterShopRequest("집밥뚝딱 안양점", "123-23-12345") + every { saveShopPort.saveOne(capture(shopSlot)) } answers { 1L } + every { shopBusinessNumberValidator.validate(any()) } answers {} + + val result = registerShopService.register(request) + + val capturedShop = shopSlot.captured + result shouldBe 1L + capturedShop.businessNumber.value shouldBe "123-23-12345" + capturedShop.title.value shouldBe "집밥뚝딱 안양점" + capturedShop.products.shouldBeEmpty() + capturedShop.reservableTimes.shouldBeEmpty() + } + + @Test + fun `service unit test - 가게 이름 형식에 맞지 않으면 예외를 발생한다`() { + val request = RegisterShopRequest("invalid!#@", "123-23-12345") + every { saveShopPort.saveOne(any()) } answers { 1L } + every { shopBusinessNumberValidator.validate(any()) } answers {} + + shouldThrow { + registerShopService.register(request) + } shouldHaveMessage "올바른 가게 이름 형식이 아닙니다.(한글, 영문, 공백, 숫자만 포함 가능)" + } + + @Test + fun `service unit test - 사업자 번호 형식에 맞지 않으면 예외를 발생한다`() { + val request = RegisterShopRequest("집밥뚝딱 안양점", "invalid-brn") + every { saveShopPort.saveOne(any()) } answers { 1L } + every { shopBusinessNumberValidator.validate(any()) } answers {} + + shouldThrow { + registerShopService.register(request) + } shouldHaveMessage "올바른 사업자번호 형식이 아닙니다." + } +} From 8c032fe07443ec4d49a62e965a12f5c447df5270 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 04:36:01 +0900 Subject: [PATCH 10/22] =?UTF-8?q?refactor=20:=20=EC=82=AC=EC=97=85?= =?UTF-8?q?=EC=9E=90=EB=B2=88=ED=98=B8=20val=EB=A1=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt index e04058c..8311334 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/Shop.kt @@ -56,8 +56,7 @@ class Shop( var products: MutableList = products protected set - var businessNumber: ShopBusinessNumber = businessNumber - protected set + val businessNumber: ShopBusinessNumber = businessNumber fun checkReservableShop() { if (status.isInvalidStatus()) { From 87ed2fae1e287194a69d4caae4c0fa99078ec2ca Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 04:36:46 +0900 Subject: [PATCH 11/22] =?UTF-8?q?fix=20:=20shop=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/data.sql | 6 +++--- .../persistence/SpringDataJpaShopPersistenceAdapterTest.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql index 80ae59c..9ffa706 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/main/resources/data.sql @@ -1,5 +1,5 @@ insert into shop -values (1, 'VALID', '집밥뚝딱 철산점'); +values (1, '123-12-12345', 'VALID', '집밥뚝딱 철산점'); insert into product values (1, '부대찌개', 15800, 1); insert into product @@ -17,7 +17,7 @@ values (1, '18:30'); insert into shop -values (2, 'VALID', '집밥뚝딱 안양점'); +values (2, '123-12-12345', 'VALID', '집밥뚝딱 안양점'); insert into product values (4, '비비고 만두', 3200, 2); insert into product @@ -30,7 +30,7 @@ insert into reservable_time values (2, '19:30'); insert into shop -values (3, 'VALID', '집밥뚝딱 숭실대입구점'); +values (3, '123-12-12345', 'VALID', '집밥뚝딱 숭실대입구점'); insert into product values (7, '왕만두', 4900, 3); insert into product diff --git a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt index a821b04..96a1258 100644 --- a/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt +++ b/mealkitary-infrastructure/adapter-persistence-spring-data-jpa/src/test/kotlin/com/mealkitary/shop/persistence/SpringDataJpaShopPersistenceAdapterTest.kt @@ -40,7 +40,7 @@ class SpringDataJpaShopPersistenceAdapterTest( val shops = adapterUnderTest.loadAllShop() shops.size shouldBe 3 - shops.get(0).title shouldBe "집밥뚝딱 철산점" + shops.get(0).title.value shouldBe "집밥뚝딱 철산점" } @Test @@ -64,7 +64,7 @@ class SpringDataJpaShopPersistenceAdapterTest( val shop = adapterUnderTest.loadOneShopById(1L) emf.persistenceUnitUtil.isLoaded(shop.products).shouldBeTrue() - shop.title shouldBe "집밥뚝딱 철산점" + shop.title.value shouldBe "집밥뚝딱 철산점" shop.products.size shouldBe 3 shop.reservableTimes.size shouldBe 4 } From a6792d2a758ae9151f667c388b0e24b82e43936e Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 05:05:23 +0900 Subject: [PATCH 12/22] =?UTF-8?q?feat=20:=20=EC=8B=A0=EA=B7=9C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EB=93=B1=EB=A1=9D=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/web/RegisterShopController.kt | 26 +++++++ .../web/request/RegisterShopWebRequest.kt | 15 ++++ .../mealkitary/WebIntegrationTestSupport.kt | 6 ++ .../shop/web/RegisterShopControllerTest.kt | 72 +++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/RegisterShopController.kt create mode 100644 mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/request/RegisterShopWebRequest.kt create mode 100644 mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/RegisterShopControllerTest.kt diff --git a/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/RegisterShopController.kt b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/RegisterShopController.kt new file mode 100644 index 0000000..3ea3fba --- /dev/null +++ b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/RegisterShopController.kt @@ -0,0 +1,26 @@ +package com.mealkitary.shop.web + +import com.mealkitary.common.utils.HttpResponseUtils +import com.mealkitary.shop.application.port.input.RegisterShopUseCase +import com.mealkitary.shop.web.request.RegisterShopWebRequest +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import javax.validation.Valid + +@RestController +@RequestMapping("/shops") +class RegisterShopController( + private val registerShopUseCase: RegisterShopUseCase +) { + + @PostMapping + fun registerShop(@Valid @RequestBody registerShopWebRequest: RegisterShopWebRequest): ResponseEntity { + val resourceId = registerShopUseCase.register(registerShopWebRequest.mapToServiceRequest()) + val location = HttpResponseUtils.createResourceUri(resourceId) + + return ResponseEntity.created(location).build() + } +} diff --git a/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/request/RegisterShopWebRequest.kt b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/request/RegisterShopWebRequest.kt new file mode 100644 index 0000000..034c616 --- /dev/null +++ b/mealkitary-api/src/main/kotlin/com/mealkitary/shop/web/request/RegisterShopWebRequest.kt @@ -0,0 +1,15 @@ +package com.mealkitary.shop.web.request + +import com.mealkitary.shop.application.port.input.RegisterShopRequest +import javax.validation.constraints.NotBlank + +data class RegisterShopWebRequest( + @field:NotBlank(message = "등록 대상 가게의 이름은 필수입니다.") + val title: String? = null, + + @field:NotBlank(message = "사업자 번호는 필수입니다.") + val brn: String? = null +) { + + fun mapToServiceRequest() = RegisterShopRequest(title!!, brn!!) +} diff --git a/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt b/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt index 337dfb9..f080e47 100644 --- a/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt +++ b/mealkitary-api/src/test/kotlin/com/mealkitary/WebIntegrationTestSupport.kt @@ -14,9 +14,11 @@ import com.mealkitary.reservation.web.ReserveProductController import com.mealkitary.shop.application.port.input.GetProductQuery import com.mealkitary.shop.application.port.input.GetReservableTimeQuery import com.mealkitary.shop.application.port.input.GetShopQuery +import com.mealkitary.shop.application.port.input.RegisterShopUseCase import com.mealkitary.shop.web.GetProductController import com.mealkitary.shop.web.GetReservableTimeController import com.mealkitary.shop.web.GetShopController +import com.mealkitary.shop.web.RegisterShopController import com.ninjasquad.springmockk.MockkBean import io.kotest.core.spec.style.AnnotationSpec import io.kotest.extensions.spring.SpringExtension @@ -30,6 +32,7 @@ import org.springframework.test.web.servlet.MockMvc PayReservationController::class, AcceptReservationController::class, RejectReservationController::class, + RegisterShopController::class, GetReservationController::class, GetShopController::class, GetReservableTimeController::class, @@ -58,6 +61,9 @@ abstract class WebIntegrationTestSupport : AnnotationSpec() { @MockkBean protected lateinit var rejectReservationUseCase: RejectReservationUseCase + @MockkBean + protected lateinit var registerShopUseCase: RegisterShopUseCase + @MockkBean protected lateinit var getReservationQuery: GetReservationQuery diff --git a/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/RegisterShopControllerTest.kt b/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/RegisterShopControllerTest.kt new file mode 100644 index 0000000..f62186a --- /dev/null +++ b/mealkitary-api/src/test/kotlin/com/mealkitary/shop/web/RegisterShopControllerTest.kt @@ -0,0 +1,72 @@ +package com.mealkitary.shop.web + +import com.mealkitary.WebIntegrationTestSupport +import com.mealkitary.shop.web.request.RegisterShopWebRequest +import io.mockk.every +import org.springframework.http.MediaType +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class RegisterShopControllerTest : WebIntegrationTestSupport() { + + @Test + fun `api integration test - registerShop`() { + every { registerShopUseCase.register(any()) } answers { 1L } + + val registerShopWebRequest = RegisterShopWebRequest("집밥뚝딱 안양점", "123-23-12345") + + mvc.perform( + MockMvcRequestBuilders.post("/shops") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(registerShopWebRequest)) + ) + .andExpect(status().isCreated) + .andExpect(header().string("Location", "http://localhost/shops/1")) + } + + @Test + fun `api integration test - 가게 이름이 누락된 경우 400 에러를 발생한다`() { + val registerShopWebRequest = RegisterShopWebRequest(brn = "123-23-12345") + + mvc.perform( + MockMvcRequestBuilders.post("/shops") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(registerShopWebRequest)) + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("400")) + .andExpect(jsonPath("$.message").value("잘못된 입력값입니다.")) + .andExpect(jsonPath("$..errors[0].field").value("title")) + .andExpect(jsonPath("$..errors[0].reason").value("등록 대상 가게의 이름은 필수입니다.")) + } + + @Test + fun `api integration test - 사업자 번호가 누락된 경우 400 에러를 발생한다`() { + val registerShopWebRequest = RegisterShopWebRequest(title = "집밥뚝딱 안양점") + + mvc.perform( + MockMvcRequestBuilders.post("/shops") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(registerShopWebRequest)) + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("400")) + .andExpect(jsonPath("$.message").value("잘못된 입력값입니다.")) + .andExpect(jsonPath("$..errors[0].field").value("brn")) + .andExpect(jsonPath("$..errors[0].reason").value("사업자 번호는 필수입니다.")) + } + + @Test + fun `api integration test - JSON 형식이 아닌 경우 400 에러가 발생한다`() { + mvc.perform( + MockMvcRequestBuilders.post("/shops") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString("}{")) + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("400")) + .andExpect(jsonPath("$.message").value("JSON 형식이 잘못되었습니다.")) + } +} From 530f7be1fe75538dcf921e2342ff4e7e8a5528b2 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 05:05:47 +0900 Subject: [PATCH 13/22] =?UTF-8?q?refactor=20:=20Long=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=EC=9D=98=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EC=8B=9D=EB=B3=84?= =?UTF-8?q?=EC=9E=90=EB=8F=84=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mealkitary/common/utils/HttpResponseUtils.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mealkitary-api/src/main/kotlin/com/mealkitary/common/utils/HttpResponseUtils.kt b/mealkitary-api/src/main/kotlin/com/mealkitary/common/utils/HttpResponseUtils.kt index fac3280..0e49722 100644 --- a/mealkitary-api/src/main/kotlin/com/mealkitary/common/utils/HttpResponseUtils.kt +++ b/mealkitary-api/src/main/kotlin/com/mealkitary/common/utils/HttpResponseUtils.kt @@ -9,13 +9,17 @@ class HttpResponseUtils { companion object { - fun createResourceUri(resourceId: UUID): URI { + fun createResourceUri(resourceId: UUID): URI = URI.create("${createBaseUri()}/$resourceId") + + fun createResourceUri(resourceId: Long): URI = URI.create("${createBaseUri()}/$resourceId") + + private fun createBaseUri(): String { val uriComponents = ServletUriComponentsBuilder.fromCurrentRequest().build() val scheme = removeSlash(uriComponents.scheme) val host = removeSlash(uriComponents.host) val path = removeSlash(uriComponents.path) - return URI.create("$scheme://$host/$path/$resourceId") + return "$scheme://$host/$path" } fun createResourceUri(path: String, resourceId: UUID): URI { From c7087cfcd904d3214e87fb1cd6f914acddcdaf3f Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 05:14:39 +0900 Subject: [PATCH 14/22] =?UTF-8?q?test=20:=20=EC=8B=A0=EA=B7=9C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EB=93=B1=EB=A1=9D=20API=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/RegisterShopControllerDocsTest.kt | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt diff --git a/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt b/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt new file mode 100644 index 0000000..4813c03 --- /dev/null +++ b/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt @@ -0,0 +1,56 @@ +package com.docs.shop + +import com.docs.RestDocsSupport +import com.mealkitary.shop.application.port.input.RegisterShopUseCase +import com.mealkitary.shop.web.RegisterShopController +import com.mealkitary.shop.web.request.RegisterShopWebRequest +import io.mockk.every +import io.mockk.mockk +import org.springframework.http.MediaType +import org.springframework.restdocs.headers.HeaderDocumentation.headerWithName +import org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders +import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest +import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse +import org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint +import org.springframework.restdocs.payload.JsonFieldType +import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath +import org.springframework.restdocs.payload.PayloadDocumentation.requestFields +import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class RegisterShopControllerDocsTest : RestDocsSupport() { + + private val registerShopUseCase = mockk() + + @Test + fun `api docs test - registerShop`() { + every { registerShopUseCase.register(any()) } answers { 1L } + + val registerShopWebRequest = RegisterShopWebRequest("집밥뚝딱 안양점", "123-23-12345") + + mvc.perform( + RestDocumentationRequestBuilders.post("/shops") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(registerShopWebRequest)) + ) + .andExpect(status().isCreated) + .andExpect(header().string("Location", "http://localhost/shops/1")) + .andDo( + MockMvcRestDocumentation.document( + "shop-post", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestFields( + fieldWithPath("title").type(JsonFieldType.STRING).description("등록 대상 가게 이름"), + fieldWithPath("brn").type(JsonFieldType.STRING).description("사업자 번호"), + ), + responseHeaders(headerWithName("Location").description("생성된 가게 리소스 URI")), + ) + ) + } + + override fun initController() = RegisterShopController(registerShopUseCase) +} From 2fb950470fda73a7111e842fcd3ed74342134ce3 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 05:15:42 +0900 Subject: [PATCH 15/22] =?UTF-8?q?refactor=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=AA=85=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/docs/reservation/GetReservationControllerDocsTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mealkitary-api/src/test/kotlin/com/docs/reservation/GetReservationControllerDocsTest.kt b/mealkitary-api/src/test/kotlin/com/docs/reservation/GetReservationControllerDocsTest.kt index 7e0d2f2..b3d57d0 100644 --- a/mealkitary-api/src/test/kotlin/com/docs/reservation/GetReservationControllerDocsTest.kt +++ b/mealkitary-api/src/test/kotlin/com/docs/reservation/GetReservationControllerDocsTest.kt @@ -88,7 +88,7 @@ class GetReservationControllerDocsTest : RestDocsSupport() { } @Test - fun `api integration test - getAllReservation`() { + fun `api docs test - getAllReservation`() { val reservationId = UUID.randomUUID() val reserveAt = LocalDateTime.of( LocalDate.of(2023, 6, 23), LocalTime.of(6, 30) From a80c051ae61d1f0aa22771a621de3de32a0dc224 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 05:18:45 +0900 Subject: [PATCH 16/22] =?UTF-8?q?docs=20:=20=EC=8B=A0=EA=B7=9C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=8C=20=EB=93=B1=EB=A1=9D=20API=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mealkitary-api/src/docs/asciidoc/shop.adoc | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mealkitary-api/src/docs/asciidoc/shop.adoc b/mealkitary-api/src/docs/asciidoc/shop.adoc index 183fa9c..526ee73 100644 --- a/mealkitary-api/src/docs/asciidoc/shop.adoc +++ b/mealkitary-api/src/docs/asciidoc/shop.adoc @@ -2,6 +2,29 @@ 가게의 상태를 변경하거나, 가게의 속한 요소(상품, 예약 가능 시간)들을 조회할 수 있습니다. +==== 신규 가게 등록 + +신규 가게를 등록합니다. + +[NOTE] +==== +신규 리소스 생성 시, 반환 헤더의 Location 값은 신규 리소스의 URI입니다. +==== + +===== 요청 + +include::{snippets}/shop-post/curl-request.adoc[] +include::{snippets}/shop-post/http-request.adoc[] +include::{snippets}/shop-post/request-fields.adoc[] + +===== 응답 + +include::{snippets}/shop-post/http-response.adoc[] + +===== 응답 헤더 + +include::{snippets}/shop-post/response-headers.adoc[] + ==== 가게 조회 가게 목록을 조회합니다. From 12b27a7e73f47bebbdfda2cc846424669ba6f7f0 Mon Sep 17 00:00:00 2001 From: le2sky Date: Tue, 26 Sep 2023 05:26:25 +0900 Subject: [PATCH 17/22] =?UTF-8?q?refactor=20:=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD,=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20private=20=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mealkitary/shop/domain/shop/ShopTitle.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt index f8a34f9..772ece0 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopTitle.kt @@ -7,10 +7,11 @@ private const val MAX_TITLE_LENGTH = 50 private const val TITLE_FORMAT = "^[a-zA-Z0-9가-힣\\s]*$" @Embeddable -class ShopTitle( +class ShopTitle private constructor( @Column(name = "title", nullable = false) val value: String ) { + companion object { fun from(title: String): ShopTitle { checkIsTitleFormat(title) @@ -26,18 +27,18 @@ class ShopTitle( } } - private fun checkTitleLength(title: String) { - if (title.length > MAX_TITLE_LENGTH) { - throw IllegalArgumentException("가게의 이름은 최대 50글자입니다.") - } - } - private fun checkIsTitleBlank(title: String) { if (title.isBlank()) { throw IllegalArgumentException("가게 이름을 입력해주세요.") } } + private fun checkTitleLength(title: String) { + if (title.length > MAX_TITLE_LENGTH) { + throw IllegalArgumentException("가게의 이름은 최대 50글자입니다.") + } + } + private fun getTitleFormatRegex() = Regex(TITLE_FORMAT) } } From c345d7447ea7ee6fa86dbf034fd3fb4d616daaca Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 27 Sep 2023 10:32:33 +0900 Subject: [PATCH 18/22] =?UTF-8?q?fix=20:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EB=B2=84=EC=A0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3f25b86..26aeb68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ ktlintVersion=11.0.0 springBootVersion=2.7.11 springDependencyManagementVersion=1.0.15.RELEASE # project -applicationVersion=0.4.0 +applicationVersion=0.5.0 projectGroup=com.mealkitary # test kotestVersion=4.4.3 From 9a35b9f1be6ffba7d9ed0d82f2b31fbeb0c026b6 Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 27 Sep 2023 11:18:09 +0900 Subject: [PATCH 19/22] =?UTF-8?q?docs=20:=20=EA=B0=80=EA=B2=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20API=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mealkitary-api/src/docs/asciidoc/shop.adoc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mealkitary-api/src/docs/asciidoc/shop.adoc b/mealkitary-api/src/docs/asciidoc/shop.adoc index 526ee73..768957a 100644 --- a/mealkitary-api/src/docs/asciidoc/shop.adoc +++ b/mealkitary-api/src/docs/asciidoc/shop.adoc @@ -84,3 +84,18 @@ include::{snippets}/shop-get-reservable-time/path-parameters.adoc[] include::{snippets}/shop-get-reservable-time/http-response.adoc[] include::{snippets}/shop-get-reservable-time/response-fields.adoc[] + + +==== 가게 상태 변경 + +가게의 상태(VALID, INVALID)를 변경합니다. 이미 가게의 상태가 VALID인 경우에는 INVALID로, INVALID인 경우에는 VALID로 변경됩니다. + +===== 요청 + +include::{snippets}/shop-post-update/curl-request.adoc[] +include::{snippets}/shop-post-update/http-request.adoc[] +include::{snippets}/shop-post-update/path-parameters.adoc[] + +===== 응답 + +include::{snippets}/shop-post-update/http-response.adoc[] From 30f35e4ad756a8423155e8df861062d9a78e905b Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 27 Sep 2023 11:29:33 +0900 Subject: [PATCH 20/22] =?UTF-8?q?chore=20:=20import=20=EB=AC=B8=20?= =?UTF-8?q?=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt b/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt index 4813c03..7006d8f 100644 --- a/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt +++ b/mealkitary-api/src/test/kotlin/com/docs/shop/RegisterShopControllerDocsTest.kt @@ -17,7 +17,6 @@ import org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPri import org.springframework.restdocs.payload.JsonFieldType import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath import org.springframework.restdocs.payload.PayloadDocumentation.requestFields -import org.springframework.test.web.servlet.result.MockMvcResultMatchers import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status From 1e7cdf2506414add2cccd7258642bfb4cdc75480 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 28 Sep 2023 02:00:22 +0900 Subject: [PATCH 21/22] =?UTF-8?q?refactor=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mealkitary/shop/domain/shop/ShopBusinessNumber.kt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt index 9d83760..14c6de7 100644 --- a/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt +++ b/mealkitary-domain/src/main/kotlin/com/mealkitary/shop/domain/shop/ShopBusinessNumber.kt @@ -5,7 +5,6 @@ import javax.persistence.Embeddable // TODO : 매번 REGEX 객체를 생성하는 것이 성능에 얼마나 영향을 미치는지 확인하고 개선 private const val BRN_FORMAT = "([0-9]{3})-?([0-9]{2})-?([0-9]{5})" -private const val INVALID_BRN_FORMAT_MESSAGE = "올바른 사업자번호 형식이 아닙니다." @Embeddable class ShopBusinessNumber private constructor( @@ -15,21 +14,14 @@ class ShopBusinessNumber private constructor( companion object { fun from(brn: String): ShopBusinessNumber { - checkIsBrnBlank(brn) checkIsBrnFormat(brn) return ShopBusinessNumber(brn) } - private fun checkIsBrnBlank(brn: String) { - if (brn.isBlank()) { - throw IllegalArgumentException(INVALID_BRN_FORMAT_MESSAGE) - } - } - private fun checkIsBrnFormat(brn: String) { if (!brn.matches(getBRNFormatRegex())) { - throw IllegalArgumentException(INVALID_BRN_FORMAT_MESSAGE) + throw IllegalArgumentException("올바른 사업자번호 형식이 아닙니다.") } } From a321979928f306c669c77a387064665375d6fc4c Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 28 Sep 2023 02:05:12 +0900 Subject: [PATCH 22/22] =?UTF-8?q?chore=20:=20=EC=8B=A0=EA=B7=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EA=B3=BC=EC=A0=95=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/mealkitary-main-develop-ci.yml | 4 +++- .github/workflows/mealkitary-test-coverage-automation.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mealkitary-main-develop-ci.yml b/.github/workflows/mealkitary-main-develop-ci.yml index 097fc00..901d7f2 100644 --- a/.github/workflows/mealkitary-main-develop-ci.yml +++ b/.github/workflows/mealkitary-main-develop-ci.yml @@ -54,6 +54,7 @@ jobs: ./mealkitary-infrastructure/adapter-persistence-spring-data-jpa/build/test-results/**/*.xml ./mealkitary-infrastructure/adapter-paymentgateway-tosspayments/build/test-results/**/*.xml ./mealkitary-infrastructure/adapter-firebase-notification/build/test-results/**/*.xml + ./mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build/test-results/**/*.xml - name: Jacoco Coverage 리포트 전송 uses: codecov/codecov-action@v3 @@ -65,7 +66,8 @@ jobs: ./mealkitary-application/build/reports/jacoco/test/jacocoTestReport.xml, ./mealkitary-infrastructure/adapter-persistence-spring-data-jpa/build/reports/jacoco/test/jacocoTestReport.xml, ./mealkitary-infrastructure/adapter-paymentgateway-tosspayments/build/reports/jacoco/test/jacocoTestReport.xml, - ./mealkitary-infrastructure/adapter-firebase-notification/build/reports/jacoco/test/jacocoTestReport.xml + ./mealkitary-infrastructure/adapter-firebase-notification/build/reports/jacoco/test/jacocoTestReport.xml, + ./mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build/reports/jacoco/test/jacocoTestReport.xml name: mealkitary-codecov verbose: true diff --git a/.github/workflows/mealkitary-test-coverage-automation.yml b/.github/workflows/mealkitary-test-coverage-automation.yml index ac958ef..acd71d2 100644 --- a/.github/workflows/mealkitary-test-coverage-automation.yml +++ b/.github/workflows/mealkitary-test-coverage-automation.yml @@ -42,6 +42,7 @@ jobs: ./mealkitary-infrastructure/adapter-persistence-spring-data-jpa/build/test-results/**/*.xml ./mealkitary-infrastructure/adapter-paymentgateway-tosspayments/build/test-results/**/*.xml ./mealkitary-infrastructure/adapter-firebase-notification/build/test-results/**/*.xml + ./mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build/test-results/**/*.xml - name: Jacoco Coverage 리포트 전송 uses: codecov/codecov-action@v3 @@ -53,6 +54,7 @@ jobs: ./mealkitary-application/build/reports/jacoco/test/jacocoTestReport.xml, ./mealkitary-infrastructure/adapter-persistence-spring-data-jpa/build/reports/jacoco/test/jacocoTestReport.xml, ./mealkitary-infrastructure/adapter-paymentgateway-tosspayments/build/reports/jacoco/test/jacocoTestReport.xml, - ./mealkitary-infrastructure/adapter-firebase-notification/build/reports/jacoco/test/jacocoTestReport.xml + ./mealkitary-infrastructure/adapter-firebase-notification/build/reports/jacoco/test/jacocoTestReport.xml, + ./mealkitary-infrastructure/business-registration-number-validator/adapter-simple-brn-validator/build/reports/jacoco/test/jacocoTestReport.xml name: mealkitary-codecov verbose: true