From f92ba7795d743b1530aa7f8392de88f52d96843b Mon Sep 17 00:00:00 2001 From: Savosin Denis Date: Tue, 6 May 2025 14:09:01 +0700 Subject: [PATCH] move and refactor city and product services to core --- core/build.gradle.kts | 3 + .../github/dannecron/demo/core/dto/City.kt | 15 ++++ .../dannecron/demo/core/dto/CityCreate.kt | 11 +++ .../github/dannecron/demo/core/dto/Product.kt | 26 ++++++ .../exceptions/AlreadyDeletedException.kt | 3 + .../core/exceptions/CityNotFoundException.kt | 3 + .../core/exceptions/ModelNotFoundException.kt | 3 + .../exceptions/ProductNotFoundException.kt | 3 + .../demo/core/services/city/CityService.kt | 19 ++++ .../core/services/city/CityServiceImpl.kt | 71 +++++++++++++++ .../core/services/product/ProductService.kt | 19 ++++ .../services}/product/ProductServiceImpl.kt | 66 +++++++------- .../dannecron/demo/core}/utils/Logger.kt | 2 +- .../services}/city/CityServiceImplTest.kt | 29 +++--- .../product/ProductServiceImplTest.kt | 89 ++++++++----------- .../services/database/city/CityService.kt | 19 ---- .../services/database/city/CityServiceImpl.kt | 60 ------------- .../database/product/ProductService.kt | 25 ------ 18 files changed, 264 insertions(+), 202 deletions(-) create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/dto/City.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/dto/CityCreate.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/dto/Product.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/AlreadyDeletedException.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/CityNotFoundException.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ModelNotFoundException.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ProductNotFoundException.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityService.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImpl.kt create mode 100644 core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductService.kt rename {src/main/kotlin/com/github/dannecron/demo/services/database => core/src/main/kotlin/com/github/dannecron/demo/core/services}/product/ProductServiceImpl.kt (52%) rename {src/main/kotlin/com/github/dannecron/demo => core/src/main/kotlin/com/github/dannecron/demo/core}/utils/Logger.kt (94%) rename {src/test/kotlin/com/github/dannecron/demo/services/database => core/src/test/kotlin/com/github/dannecron/demo/core/services}/city/CityServiceImplTest.kt (65%) rename {src/test/kotlin/com/github/dannecron/demo/services/database => core/src/test/kotlin/com/github/dannecron/demo/core/services}/product/ProductServiceImplTest.kt (57%) delete mode 100644 src/main/kotlin/com/github/dannecron/demo/services/database/city/CityService.kt delete mode 100644 src/main/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImpl.kt delete mode 100644 src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductService.kt diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 674b9a2..877c77a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -2,6 +2,9 @@ group = "com.github.dannecron.demo" version = "single-version" dependencies { + implementation(project(":db")) + + implementation(rootProject.libs.spring.boot.starter.jdbc) implementation(rootProject.libs.spring.boot.starter.validation) implementation(rootProject.libs.json.schema.validator) } diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/dto/City.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/dto/City.kt new file mode 100644 index 0000000..dcbb55e --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/dto/City.kt @@ -0,0 +1,15 @@ +package com.github.dannecron.demo.core.dto + +import java.time.OffsetDateTime +import java.util.UUID + +data class City( + val id: Long, + val guid: UUID, + val name: String, + val createdAt: OffsetDateTime, + val updatedAt: OffsetDateTime?, + val deletedAt: OffsetDateTime?, +) { + fun isDeleted(): Boolean = deletedAt != null +} diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/dto/CityCreate.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/dto/CityCreate.kt new file mode 100644 index 0000000..9a42b0a --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/dto/CityCreate.kt @@ -0,0 +1,11 @@ +package com.github.dannecron.demo.core.dto + +import java.time.OffsetDateTime + +data class CityCreate( + val guid: String, + val name: String, + val createdAt: OffsetDateTime, + val updatedAt: OffsetDateTime?, + val deletedAt: OffsetDateTime?, +) diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/dto/Product.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/dto/Product.kt new file mode 100644 index 0000000..afb9961 --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/dto/Product.kt @@ -0,0 +1,26 @@ +package com.github.dannecron.demo.core.dto + +import java.time.OffsetDateTime +import java.util.UUID +import kotlin.math.pow +import kotlin.math.roundToInt + +data class Product( + val id: Long, + val guid: UUID, + val name: String, + val description: String?, + val price: Long, + val createdAt: OffsetDateTime, + val updatedAt: OffsetDateTime?, + val deletedAt: OffsetDateTime?, +) { + fun getPriceDouble(): Double = (price.toDouble() / 100).roundTo(2) + + fun isDeleted(): Boolean = deletedAt != null + + private fun Double.roundTo(numFractionDigits: Int): Double { + val factor = 10.0.pow(numFractionDigits.toDouble()) + return (this * factor).roundToInt() / factor + } +} diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/AlreadyDeletedException.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/AlreadyDeletedException.kt new file mode 100644 index 0000000..958f0db --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/AlreadyDeletedException.kt @@ -0,0 +1,3 @@ +package com.github.dannecron.demo.core.exceptions + +class AlreadyDeletedException: RuntimeException() diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/CityNotFoundException.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/CityNotFoundException.kt new file mode 100644 index 0000000..ccb6b62 --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/CityNotFoundException.kt @@ -0,0 +1,3 @@ +package com.github.dannecron.demo.core.exceptions + +class CityNotFoundException: ModelNotFoundException("city") diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ModelNotFoundException.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ModelNotFoundException.kt new file mode 100644 index 0000000..46d96f5 --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ModelNotFoundException.kt @@ -0,0 +1,3 @@ +package com.github.dannecron.demo.core.exceptions + +open class ModelNotFoundException(entityName: String): RuntimeException("$entityName not found") diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ProductNotFoundException.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ProductNotFoundException.kt new file mode 100644 index 0000000..e180b3c --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/exceptions/ProductNotFoundException.kt @@ -0,0 +1,3 @@ +package com.github.dannecron.demo.core.exceptions + +class ProductNotFoundException: ModelNotFoundException("product") diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityService.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityService.kt new file mode 100644 index 0000000..18cd2ce --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityService.kt @@ -0,0 +1,19 @@ +package com.github.dannecron.demo.core.services.city + +import com.github.dannecron.demo.core.dto.City +import com.github.dannecron.demo.core.dto.CityCreate +import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException +import com.github.dannecron.demo.core.exceptions.CityNotFoundException +import org.springframework.stereotype.Service +import java.util.UUID + +@Service +interface CityService { + fun findByGuid(guid: UUID): City? + + fun create(name: String): City + fun create(cityCreate: CityCreate): City + + @Throws(CityNotFoundException::class, AlreadyDeletedException::class) + fun delete(guid: UUID): City +} diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImpl.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImpl.kt new file mode 100644 index 0000000..23ac7c6 --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImpl.kt @@ -0,0 +1,71 @@ +package com.github.dannecron.demo.core.services.city + +import com.github.dannecron.demo.core.dto.City +import com.github.dannecron.demo.core.dto.CityCreate +import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException +import com.github.dannecron.demo.core.exceptions.CityNotFoundException +import com.github.dannecron.demo.core.services.generation.CommonGenerator +import com.github.dannecron.demo.db.entity.CityEntity +import com.github.dannecron.demo.db.repository.CityRepository +import org.springframework.stereotype.Service +import java.time.OffsetDateTime +import java.util.UUID + +@Service +class CityServiceImpl( + private val cityRepository: CityRepository, + private val commonGenerator: CommonGenerator, +): CityService { + override fun findByGuid(guid: UUID): City? = cityRepository.findByGuid(guid)?.toCore() + + override fun create(name: String): City = CityEntity( + id = null, + guid = commonGenerator.generateUUID(), + name = name, + createdAt = commonGenerator.generateCurrentTime(), + updatedAt = null, + deletedAt = null, + ).let(cityRepository::save).toCore() + + override fun create(cityCreate: CityCreate): City = CityEntity( + id = null, + guid = UUID.fromString(cityCreate.guid), + name = cityCreate.name, + createdAt = cityCreate.createdAt, + updatedAt = cityCreate.updatedAt, + deletedAt = cityCreate.deletedAt, + ).let(cityRepository::save).toCore() + + @Throws(CityNotFoundException::class, AlreadyDeletedException::class) + override fun delete(guid: UUID): City { + val city = findByGuid(guid) ?: throw CityNotFoundException() + + if (city.isDeleted()) { + throw AlreadyDeletedException() + } + + return cityRepository.save( + city.copy( + deletedAt = OffsetDateTime.now(), + ).toEntity() + ).toCore() + } + + private fun CityEntity.toCore() = City( + id = id!!, + guid = guid, + name = name, + createdAt = createdAt, + updatedAt = updatedAt, + deletedAt = deletedAt, + ) + + private fun City.toEntity() = CityEntity( + id = id, + guid = guid, + name = name, + createdAt = createdAt, + updatedAt = updatedAt, + deletedAt = deletedAt, + ) +} diff --git a/core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductService.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductService.kt new file mode 100644 index 0000000..129b3c9 --- /dev/null +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductService.kt @@ -0,0 +1,19 @@ +package com.github.dannecron.demo.core.services.product + +import com.github.dannecron.demo.core.dto.Product +import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException +import com.github.dannecron.demo.core.exceptions.ProductNotFoundException +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import java.util.UUID + +interface ProductService { + fun findByGuid(guid: UUID): Product? + + fun findAll(pageable: Pageable): Page + + fun create(name: String, price: Long, description: String?): Product + + @Throws(ProductNotFoundException::class, AlreadyDeletedException::class) + fun delete(guid: UUID): Product +} diff --git a/src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductServiceImpl.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductServiceImpl.kt similarity index 52% rename from src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductServiceImpl.kt rename to core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductServiceImpl.kt index 379e635..76034a5 100644 --- a/src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductServiceImpl.kt +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/services/product/ProductServiceImpl.kt @@ -1,30 +1,26 @@ -package com.github.dannecron.demo.services.database.product +package com.github.dannecron.demo.core.services.product +import com.github.dannecron.demo.core.dto.Product +import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException +import com.github.dannecron.demo.core.exceptions.ProductNotFoundException import com.github.dannecron.demo.core.services.generation.CommonGenerator -import com.github.dannecron.demo.db.entity.Product +import com.github.dannecron.demo.core.utils.LoggerDelegate +import com.github.dannecron.demo.db.entity.ProductEntity import com.github.dannecron.demo.db.repository.ProductRepository -import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException -import com.github.dannecron.demo.services.database.exceptions.ProductNotFoundException -import com.github.dannecron.demo.services.kafka.Producer -import com.github.dannecron.demo.services.kafka.dto.ProductDto -import com.github.dannecron.demo.services.kafka.exceptions.InvalidArgumentException -import com.github.dannecron.demo.utils.LoggerDelegate import net.logstash.logback.marker.Markers import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import java.time.format.DateTimeFormatter import java.util.UUID @Service class ProductServiceImpl( private val productRepository: ProductRepository, - private val producer: Producer, private val commonGenerator: CommonGenerator, ): ProductService { private val logger by LoggerDelegate() - override fun findByGuid(guid: UUID): Product? = productRepository.findByGuid(guid) + override fun findByGuid(guid: UUID): Product? = productRepository.findByGuid(guid)?.toCore() .also { logger.debug( Markers.appendEntries(mapOf("guid" to guid, "idResult" to it?.id)), @@ -33,9 +29,10 @@ class ProductServiceImpl( } override fun findAll(pageable: Pageable): Page = productRepository.findAll(pageable) + .map { it.toCore() } - override fun create(name: String, price: Long, description: String?): Product { - val product = Product( + override fun create(name: String, price: Long, description: String?): Product = + ProductEntity( id = null, guid = commonGenerator.generateUUID(), name = name, @@ -44,11 +41,9 @@ class ProductServiceImpl( createdAt = commonGenerator.generateCurrentTime(), updatedAt = null, deletedAt = null, - ) - - return productRepository.save(product) - } + ).let(productRepository::save).toCore() + @Throws(ProductNotFoundException::class, AlreadyDeletedException::class) override fun delete(guid: UUID): Product { val product = findByGuid(guid) ?: throw ProductNotFoundException() @@ -56,27 +51,32 @@ class ProductServiceImpl( throw AlreadyDeletedException() } - val deletedProduct = product.copy( + return product.copy( deletedAt = commonGenerator.generateCurrentTime(), - ) - - return productRepository.save(deletedProduct) + ).toEntity() + .let(productRepository::save) + .toCore() } - override fun syncToKafka(guid: UUID, topic: String?) { - val product = findByGuid(guid) ?: throw ProductNotFoundException() - - producer.produceProductSync(product.toKafkaDto()) - } - - private fun Product.toKafkaDto() = ProductDto( - id = id ?: throw InvalidArgumentException("product.id"), - guid = guid.toString(), + private fun ProductEntity.toCore() = Product( + id = id!!, + guid = guid, name = name, description = description, price = price, - createdAt = createdAt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), - updatedAt = updatedAt?.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), - deletedAt = deletedAt?.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), + createdAt = createdAt, + updatedAt = updatedAt, + deletedAt = deletedAt, + ) + + private fun Product.toEntity() = ProductEntity( + id = id, + guid = guid, + name = name, + description = description, + price = price, + createdAt = createdAt, + updatedAt = updatedAt, + deletedAt = deletedAt, ) } diff --git a/src/main/kotlin/com/github/dannecron/demo/utils/Logger.kt b/core/src/main/kotlin/com/github/dannecron/demo/core/utils/Logger.kt similarity index 94% rename from src/main/kotlin/com/github/dannecron/demo/utils/Logger.kt rename to core/src/main/kotlin/com/github/dannecron/demo/core/utils/Logger.kt index 5a0841e..92cf592 100644 --- a/src/main/kotlin/com/github/dannecron/demo/utils/Logger.kt +++ b/core/src/main/kotlin/com/github/dannecron/demo/core/utils/Logger.kt @@ -1,4 +1,4 @@ -package com.github.dannecron.demo.utils +package com.github.dannecron.demo.core.utils import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/src/test/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImplTest.kt b/core/src/test/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImplTest.kt similarity index 65% rename from src/test/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImplTest.kt rename to core/src/test/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImplTest.kt index bbd3c73..8373523 100644 --- a/src/test/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImplTest.kt +++ b/core/src/test/kotlin/com/github/dannecron/demo/core/services/city/CityServiceImplTest.kt @@ -1,9 +1,10 @@ -package com.github.dannecron.demo.services.database.city +package com.github.dannecron.demo.core.services.city +import com.github.dannecron.demo.core.dto.City +import com.github.dannecron.demo.core.dto.CityCreate import com.github.dannecron.demo.core.services.generation.CommonGenerator -import com.github.dannecron.demo.db.entity.City +import com.github.dannecron.demo.db.entity.CityEntity import com.github.dannecron.demo.db.repository.CityRepository -import com.github.dannecron.demo.services.kafka.dto.CityCreateDto import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock @@ -11,7 +12,6 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter import java.util.UUID import kotlin.test.Test import kotlin.test.assertEquals @@ -27,6 +27,14 @@ class CityServiceImplTest { private val cityRepository: CityRepository = mock() private val cityServiceImpl = CityServiceImpl(cityRepository, commonGenerator) + private val cityEntity = CityEntity( + id = 1000, + guid = mockGuid, + name = "name", + createdAt = mockCurrentTime, + updatedAt = null, + deletedAt = null, + ) private val city = City( id = 1000, guid = mockGuid, @@ -38,33 +46,34 @@ class CityServiceImplTest { @Test fun `create - by name`() { - whenever(cityRepository.save(any())).thenReturn(city) + whenever(cityRepository.save(any())).thenReturn(cityEntity) val result = cityServiceImpl.create("name") assertEquals(city, result) - verify(cityRepository, times(1)).save(city.copy(id = null)) + verify(cityRepository, times(1)).save(cityEntity.copy(id = null)) } @Test fun `create - by dto`() { val cityGuid = UUID.randomUUID() val createdAt = OffsetDateTime.now() - val cityCreate = CityCreateDto( + val cityCreate = CityCreate( guid = cityGuid.toString(), name = "name", - createdAt = createdAt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), + createdAt = createdAt, updatedAt = null, deletedAt = null, ) + val expectedCityEntity = cityEntity.copy(guid = cityGuid, createdAt = createdAt) val expectedCity = city.copy(guid = cityGuid, createdAt = createdAt) - whenever(cityRepository.save(any())).thenReturn(expectedCity) + whenever(cityRepository.save(any())).thenReturn(expectedCityEntity) val result = cityServiceImpl.create(cityCreate) assertEquals(expectedCity, result) - verify(cityRepository, times(1)).save(expectedCity.copy(id = null)) + verify(cityRepository, times(1)).save(expectedCityEntity.copy(id = null)) } } diff --git a/src/test/kotlin/com/github/dannecron/demo/services/database/product/ProductServiceImplTest.kt b/core/src/test/kotlin/com/github/dannecron/demo/core/services/product/ProductServiceImplTest.kt similarity index 57% rename from src/test/kotlin/com/github/dannecron/demo/services/database/product/ProductServiceImplTest.kt rename to core/src/test/kotlin/com/github/dannecron/demo/core/services/product/ProductServiceImplTest.kt index 5b563ea..acc5731 100644 --- a/src/test/kotlin/com/github/dannecron/demo/services/database/product/ProductServiceImplTest.kt +++ b/core/src/test/kotlin/com/github/dannecron/demo/core/services/product/ProductServiceImplTest.kt @@ -1,12 +1,11 @@ -package com.github.dannecron.demo.services.database.product +package com.github.dannecron.demo.core.services.product +import com.github.dannecron.demo.core.dto.Product +import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException +import com.github.dannecron.demo.core.exceptions.ProductNotFoundException import com.github.dannecron.demo.core.services.generation.CommonGenerator -import com.github.dannecron.demo.db.entity.Product +import com.github.dannecron.demo.db.entity.ProductEntity import com.github.dannecron.demo.db.repository.ProductRepository -import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException -import com.github.dannecron.demo.services.database.exceptions.ProductNotFoundException -import com.github.dannecron.demo.services.kafka.Producer -import com.github.dannecron.demo.services.kafka.dto.ProductDto import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.mockito.kotlin.any @@ -15,10 +14,8 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter import java.util.UUID import kotlin.test.assertEquals @@ -27,7 +24,6 @@ class ProductServiceImplTest { private val mockCurrentTime = OffsetDateTime.now() private val productRepository: ProductRepository = mock() - private val producer: Producer = mock() private val commonGenerator: CommonGenerator = mock { on { generateUUID() } doReturn mockGuid on { generateCurrentTime() } doReturn mockCurrentTime @@ -35,72 +31,79 @@ class ProductServiceImplTest { private val productService = ProductServiceImpl( productRepository = productRepository, - producer = producer, commonGenerator = commonGenerator, ) private val guid = UUID.randomUUID() + private val productEntity = ProductEntity( + id = 123, + guid = guid, + name = "name", + description = "description", + price = 10050, + createdAt = mockCurrentTime.minusDays(1), + updatedAt = mockCurrentTime.minusHours(2), + deletedAt = null, + ) private val product = Product( id = 123, guid = guid, name = "name", description = "description", price = 10050, - createdAt = OffsetDateTime.now().minusDays(1), - updatedAt = OffsetDateTime.now().minusHours(2), - deletedAt = null, - ) - private val kafkaProductDto = ProductDto( - id = 123, - guid = guid.toString(), - name = "name", - description = "description", - price = 10050, - createdAt = product.createdAt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), - updatedAt = product.updatedAt!!.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), + createdAt = mockCurrentTime.minusDays(1), + updatedAt = mockCurrentTime.minusHours(2), deletedAt = null, ) @Test fun create() { - val expectedProductForCreation = product.copy( + val expectedProductForCreation = productEntity.copy( id = null, guid = mockGuid, createdAt = mockCurrentTime, updatedAt = null, ) - val expectedCreatedProduct = expectedProductForCreation.copy(id = 1) + val expectedCreatedProductEntity = expectedProductForCreation.copy(id = 1) + val expectedProduct = product.copy( + id = 1, + guid = mockGuid, + createdAt = mockCurrentTime, + updatedAt = null, + ) - whenever(productRepository.save(any())).thenReturn(expectedCreatedProduct) + whenever(productRepository.save(any())).thenReturn(expectedCreatedProductEntity) val result = productService.create( name = "name", price = 10050, description = "description", ) - assertEquals(expectedCreatedProduct, result) + assertEquals(expectedProduct, result) verify(productRepository, times(1)).save(expectedProductForCreation) } @Test fun `delete - success`() { - val deletedProduct = product.copy( + val deletedProductEntity = productEntity.copy( deletedAt = mockCurrentTime, ) - whenever(productRepository.findByGuid(any())).thenReturn(product) - whenever(productRepository.save(any())).thenReturn(deletedProduct) + val expectedProduct = product.copy(deletedAt = mockCurrentTime) + + whenever(productRepository.findByGuid(any())).thenReturn(productEntity) + whenever(productRepository.save(any())).thenReturn(deletedProductEntity) val result = productService.delete(guid) - assertEquals(deletedProduct, result) + assertEquals(expectedProduct, result) verify(productRepository, times(1)).findByGuid(guid) - verify(productRepository, times(1)).save(deletedProduct) + verify(productRepository, times(1)).save(deletedProductEntity) } @Test fun `delete - fail - already deleted`() { - val deletedProduct = product.copy( + val deletedProduct = productEntity.copy( deletedAt = mockCurrentTime, ) whenever(productRepository.findByGuid(any())).thenReturn(deletedProduct) @@ -124,26 +127,4 @@ class ProductServiceImplTest { verify(productRepository, times(1)).findByGuid(guid) verify(productRepository, never()).save(any()) } - - @Test - fun `syncToKafka - success`() { - whenever(productRepository.findByGuid(any())) doReturn product - - productService.syncToKafka(guid, null) - - verify(productRepository, times(1)).findByGuid(guid) - verify(producer, times(1)).produceProductSync(kafkaProductDto) - } - - @Test - fun `syncToKafka - not found`() { - whenever(productRepository.findByGuid(any())) doReturn null - - assertThrows { - productService.syncToKafka(guid, null) - } - - verify(productRepository, times(1)).findByGuid(guid) - verifyNoInteractions(producer) - } } diff --git a/src/main/kotlin/com/github/dannecron/demo/services/database/city/CityService.kt b/src/main/kotlin/com/github/dannecron/demo/services/database/city/CityService.kt deleted file mode 100644 index ff34ed4..0000000 --- a/src/main/kotlin/com/github/dannecron/demo/services/database/city/CityService.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.dannecron.demo.services.database.city - -import com.github.dannecron.demo.db.entity.City -import com.github.dannecron.demo.services.database.exceptions.CityNotFoundException -import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException -import com.github.dannecron.demo.services.kafka.dto.CityCreateDto -import org.springframework.stereotype.Service -import java.util.* - -@Service -interface CityService { - fun findByGuid(guid: UUID): City? - - fun create(name: String): City - fun create(kafkaCityDto: CityCreateDto): City - - @Throws(CityNotFoundException::class, AlreadyDeletedException::class) - fun delete(guid: UUID): City -} diff --git a/src/main/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImpl.kt b/src/main/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImpl.kt deleted file mode 100644 index 95c683e..0000000 --- a/src/main/kotlin/com/github/dannecron/demo/services/database/city/CityServiceImpl.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.github.dannecron.demo.services.database.city - -import com.github.dannecron.demo.core.services.generation.CommonGenerator -import com.github.dannecron.demo.db.entity.City -import com.github.dannecron.demo.db.repository.CityRepository -import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException -import com.github.dannecron.demo.services.database.exceptions.CityNotFoundException -import com.github.dannecron.demo.services.kafka.dto.CityCreateDto -import org.springframework.stereotype.Service -import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter -import java.util.UUID - -@Service -class CityServiceImpl( - private val cityRepository: CityRepository, - private val commonGenerator: CommonGenerator, -): CityService { - override fun findByGuid(guid: UUID): City? = cityRepository.findByGuid(guid) - - override fun create(name: String): City = City( - id = null, - guid = commonGenerator.generateUUID(), - name = name, - createdAt = commonGenerator.generateCurrentTime(), - updatedAt = null, - deletedAt = null, - ).let { - cityRepository.save(it) - } - - override fun create(kafkaCityDto: CityCreateDto): City = City( - id = null, - guid = UUID.fromString(kafkaCityDto.guid), - name = kafkaCityDto.name, - createdAt = OffsetDateTime.parse(kafkaCityDto.createdAt, DateTimeFormatter.ISO_OFFSET_DATE_TIME), - updatedAt = kafkaCityDto.deletedAt?.let { - OffsetDateTime.parse(it, DateTimeFormatter.ISO_OFFSET_DATE_TIME) - }, - deletedAt = kafkaCityDto.deletedAt?.let { - OffsetDateTime.parse(it, DateTimeFormatter.ISO_OFFSET_DATE_TIME) - }, - ).let { - cityRepository.save(it) - } - - override fun delete(guid: UUID): City { - val city = findByGuid(guid) ?: throw CityNotFoundException() - - if (city.isDeleted()) { - throw AlreadyDeletedException() - } - - return cityRepository.save( - city.copy( - deletedAt = OffsetDateTime.now(), - ) - ) - } -} diff --git a/src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductService.kt b/src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductService.kt deleted file mode 100644 index 5fdd457..0000000 --- a/src/main/kotlin/com/github/dannecron/demo/services/database/product/ProductService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.dannecron.demo.services.database.product - -import com.github.dannecron.demo.db.entity.Product -import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException -import com.github.dannecron.demo.services.database.exceptions.ProductNotFoundException -import com.github.dannecron.demo.services.kafka.exceptions.InvalidArgumentException -import org.springframework.data.domain.Page -import org.springframework.data.domain.Pageable -import org.springframework.stereotype.Service -import java.util.UUID - -@Service -interface ProductService { - fun findByGuid(guid: UUID): Product? - - fun findAll(pageable: Pageable): Page - - fun create(name: String, price: Long, description: String?): Product - - @Throws(ProductNotFoundException::class, AlreadyDeletedException::class) - fun delete(guid: UUID): Product - - @Throws(ProductNotFoundException::class, InvalidArgumentException::class) - fun syncToKafka(guid: UUID, topic: String?) -}