mirror of
https://github.com/Dannecron/spring-boot-demo.git
synced 2025-12-25 16:22:35 +03:00
move producing to sub-project
This commit is contained in:
@@ -78,7 +78,9 @@ subprojects {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":edge-contracts"))
|
||||
implementation(project(":db"))
|
||||
implementation(project(":edge-producing"))
|
||||
implementation(project(":core"))
|
||||
implementation(project(":edge-consuming"))
|
||||
|
||||
@@ -104,7 +106,9 @@ dependencies {
|
||||
|
||||
kover(project(":edge-consuming"))
|
||||
kover(project(":core"))
|
||||
kover(project(":edge-producing"))
|
||||
kover(project(":db"))
|
||||
kover(project(":edge-contracts"))
|
||||
}
|
||||
|
||||
tasks.bootJar {
|
||||
|
||||
10
edge-producing/build.gradle.kts
Normal file
10
edge-producing/build.gradle.kts
Normal file
@@ -0,0 +1,10 @@
|
||||
dependencies {
|
||||
implementation(project(":edge-contracts"))
|
||||
|
||||
implementation(rootProject.libs.jackson.datatype.jsr)
|
||||
implementation(rootProject.libs.jackson.module.kotlin)
|
||||
implementation(rootProject.libs.spring.boot.starter.validation)
|
||||
implementation(rootProject.libs.spring.cloud.starter.streamKafka)
|
||||
|
||||
testImplementation(rootProject.libs.spring.cloud.streamTestBinder)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.dannecron.demo.services.kafka.dto
|
||||
package com.github.dannecron.demo.edgeproducing.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package com.github.dannecron.demo.services.kafka.exceptions
|
||||
package com.github.dannecron.demo.edgeproducing.exceptions
|
||||
|
||||
class InvalidArgumentException(argName: String): RuntimeException("invalid argument $argName")
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.dannecron.demo.edgeproducing.producer
|
||||
|
||||
import com.github.dannecron.demo.edgeproducing.dto.ProductDto
|
||||
import com.github.dannecron.demo.edgeproducing.exceptions.InvalidArgumentException
|
||||
|
||||
interface ProductProducer {
|
||||
@Throws(InvalidArgumentException::class)
|
||||
fun produceProductSync(product: ProductDto)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.github.dannecron.demo.services.kafka
|
||||
package com.github.dannecron.demo.edgeproducing.producer
|
||||
|
||||
import com.github.dannecron.demo.core.services.validation.SchemaValidator
|
||||
import com.github.dannecron.demo.services.kafka.dto.ProductDto
|
||||
import com.github.dannecron.demo.edgecontracts.validation.SchemaValidator
|
||||
import com.github.dannecron.demo.edgeproducing.dto.ProductDto
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import org.springframework.cloud.stream.function.StreamBridge
|
||||
@@ -9,10 +9,10 @@ import org.springframework.messaging.support.MessageBuilder
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class ProducerImpl(
|
||||
class ProductProducerImpl(
|
||||
private val streamBridge: StreamBridge,
|
||||
private val schemaValidator: SchemaValidator,
|
||||
): Producer {
|
||||
): ProductProducer {
|
||||
private companion object {
|
||||
private const val BINDING_NAME_PRODUCT_SYNC = "productSyncProducer"
|
||||
private const val SCHEMA_KAFKA_PRODUCT_SYNC = "kafka-product-sync"
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.github.dannecron.demo.services.kafka
|
||||
package com.github.dannecron.demo.edgeproducing.producer
|
||||
|
||||
import com.github.dannecron.demo.core.services.validation.SchemaValidator
|
||||
import com.github.dannecron.demo.services.kafka.dto.ProductDto
|
||||
import com.github.dannecron.demo.edgecontracts.validation.SchemaValidator
|
||||
import com.github.dannecron.demo.edgeproducing.dto.ProductDto
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.eq
|
||||
@@ -17,12 +18,11 @@ import org.springframework.messaging.Message
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.UUID
|
||||
import kotlin.test.Test
|
||||
|
||||
class ProducerImplTest {
|
||||
class ProductProducerImplTest {
|
||||
private val streamBridge: StreamBridge = mock()
|
||||
private val schemaValidator: SchemaValidator = mock()
|
||||
private val producerImpl = ProducerImpl(
|
||||
private val producerImpl = ProductProducerImpl(
|
||||
streamBridge = streamBridge,
|
||||
schemaValidator = schemaValidator,
|
||||
)
|
||||
@@ -5,3 +5,5 @@ rootProject.name = "demo"
|
||||
include("db")
|
||||
include("core")
|
||||
include("edge-consuming")
|
||||
include("edge-producing")
|
||||
include("edge-contracts")
|
||||
|
||||
@@ -3,21 +3,18 @@ package com.github.dannecron.demo.config
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
import com.github.dannecron.demo.core.config.properties.ValidationProperties
|
||||
import io.ktor.client.engine.HttpClientEngine
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.micrometer.observation.ObservationRegistry
|
||||
import io.micrometer.observation.aop.ObservedAspect
|
||||
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import com.github.dannecron.demo.services.neko.Client as NekoClient
|
||||
import com.github.dannecron.demo.services.neko.ClientImpl as NekoClientImpl
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ValidationProperties::class)
|
||||
class AppConfig {
|
||||
@Bean
|
||||
fun objectMapper(): ObjectMapper = ObjectMapper().apply {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.github.dannecron.demo.services
|
||||
|
||||
|
||||
import com.github.dannecron.demo.core.exceptions.ProductNotFoundException
|
||||
import com.github.dannecron.demo.services.kafka.exceptions.InvalidArgumentException
|
||||
import java.util.UUID
|
||||
|
||||
interface ProductSyncService {
|
||||
|
||||
@Throws(ProductNotFoundException::class, InvalidArgumentException::class)
|
||||
fun syncToKafka(guid: UUID, topic: String?)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.github.dannecron.demo.services
|
||||
|
||||
import com.github.dannecron.demo.core.dto.Product
|
||||
import com.github.dannecron.demo.core.exceptions.ProductNotFoundException
|
||||
import com.github.dannecron.demo.core.services.product.ProductService
|
||||
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 org.springframework.stereotype.Service
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.UUID
|
||||
|
||||
@Service
|
||||
class ProductSyncServiceImpl(
|
||||
private val productService: ProductService,
|
||||
private val producer: Producer,
|
||||
) : ProductSyncService {
|
||||
|
||||
@Throws(ProductNotFoundException::class, InvalidArgumentException::class)
|
||||
override fun syncToKafka(guid: UUID, topic: String?) {
|
||||
val product = productService.findByGuid(guid) ?: throw ProductNotFoundException()
|
||||
|
||||
producer.produceProductSync(product.toKafkaDto())
|
||||
}
|
||||
|
||||
private fun Product.toKafkaDto() = ProductDto(
|
||||
id = id,
|
||||
guid = guid.toString(),
|
||||
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),
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.github.dannecron.demo.services.kafka
|
||||
|
||||
import com.github.dannecron.demo.services.kafka.dto.ProductDto
|
||||
import com.github.dannecron.demo.services.kafka.exceptions.InvalidArgumentException
|
||||
|
||||
interface Producer {
|
||||
@Throws(InvalidArgumentException::class)
|
||||
fun produceProductSync(product: ProductDto)
|
||||
}
|
||||
@@ -1,15 +1,8 @@
|
||||
package com.github.dannecron.demo
|
||||
|
||||
import com.github.dannecron.demo.core.config.properties.ValidationProperties
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
|
||||
open class BaseUnitTest {
|
||||
@TestConfiguration
|
||||
class TestConfig {
|
||||
@Bean
|
||||
fun validationProperties(): ValidationProperties = ValidationProperties(
|
||||
schema = mapOf("kafka-product-sync" to "kafka/product/sync.json"),
|
||||
)
|
||||
}
|
||||
class TestConfig
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.github.dannecron.demo.services
|
||||
|
||||
import com.github.dannecron.demo.core.dto.Product
|
||||
import com.github.dannecron.demo.core.exceptions.ProductNotFoundException
|
||||
import com.github.dannecron.demo.core.services.product.ProductService
|
||||
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
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
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
|
||||
|
||||
class ProductSyncServiceImplTest {
|
||||
private val productService: ProductService = mock()
|
||||
private val producer: Producer = mock()
|
||||
|
||||
private val productSyncService = ProductSyncServiceImpl(
|
||||
productService = productService,
|
||||
producer = producer
|
||||
)
|
||||
|
||||
private val guid = UUID.randomUUID()
|
||||
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),
|
||||
deletedAt = null,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `syncToKafka - success`() {
|
||||
whenever(productService.findByGuid(any())).thenReturn(product)
|
||||
|
||||
productSyncService.syncToKafka(guid, null)
|
||||
|
||||
verify(productService, times(1)).findByGuid(guid)
|
||||
verify(producer, times(1)).produceProductSync(kafkaProductDto)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `syncToKafka - not found`() {
|
||||
whenever(productService.findByGuid(any())) doReturn null
|
||||
|
||||
assertThrows<ProductNotFoundException> {
|
||||
productSyncService.syncToKafka(guid, null)
|
||||
}
|
||||
|
||||
verify(productService, times(1)).findByGuid(guid)
|
||||
verifyNoInteractions(producer)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:tc:postgresql:14-alpine:///test
|
||||
hikari:
|
||||
maximum-pool-size: 2
|
||||
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create
|
||||
|
||||
kafka:
|
||||
bootstrap-servers: localhost:3392
|
||||
producer:
|
||||
product:
|
||||
default-sync-topic: demo-product-sync
|
||||
consumer:
|
||||
group-id: demo-consumer
|
||||
topics: demo-city-sync
|
||||
auto-offset-reset: earliest
|
||||
auto-startup: true
|
||||
Reference in New Issue
Block a user