diff --git a/build.gradle.kts b/build.gradle.kts index 8a13719..995f84c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,9 @@ plugins { id("org.springframework.boot") version "3.2.4" id("io.spring.dependency-management") version "1.1.4" + jacoco + kotlin("jvm") version "2.0.20" kotlin("plugin.jpa") version "1.9.23" kotlin("plugin.serialization") version "2.0.20" @@ -20,15 +22,21 @@ repositories { } dependencies { + api("org.springframework.boot:spring-boot-starter-data-jdbc") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.flywaydb:flyway-core") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-jdbc") implementation("org.springframework.boot:spring-boot-starter-mustache") implementation("org.springframework.boot:spring-boot-starter-web") + developmentOnly("org.springframework.boot:spring-boot-devtools") + runtimeOnly("org.postgresql:postgresql") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0") testImplementation("org.springframework.boot:spring-boot-starter-test") diff --git a/src/main/kotlin/com/example/demo/models/Customer.kt b/src/main/kotlin/com/example/demo/models/Customer.kt index 9813cd0..ddda4b2 100644 --- a/src/main/kotlin/com/example/demo/models/Customer.kt +++ b/src/main/kotlin/com/example/demo/models/Customer.kt @@ -12,5 +12,5 @@ data class Customer(val name: String, val city: City, val orders: List) { fun getOrderedProducts(): Set = orders.flatMap { order -> order.products }.toSet() - fun getTotalOrderPrice(): Double = orders.flatMap { ord -> ord.products }.sumOf { pr -> pr.price } + fun getTotalOrderPrice(): Double = orders.flatMap { ord -> ord.products }.sumOf { pr -> pr.getPriceDouble()} } \ No newline at end of file diff --git a/src/main/kotlin/com/example/demo/models/Product.kt b/src/main/kotlin/com/example/demo/models/Product.kt index 45faab4..a87be06 100644 --- a/src/main/kotlin/com/example/demo/models/Product.kt +++ b/src/main/kotlin/com/example/demo/models/Product.kt @@ -1,3 +1,31 @@ package com.example.demo.models -data class Product(val name: String, val price: Double) \ No newline at end of file + +import com.example.demo.models.serializables.OffsetDateTimeSerialization +import com.example.demo.models.serializables.UuidSerialization +import com.example.demo.utils.roundTo +import jakarta.persistence.Column +import jakarta.persistence.Id +import kotlinx.serialization.Serializable +import org.springframework.data.relational.core.mapping.Table +import java.time.OffsetDateTime +import java.util.* + +@Table(value = "product", schema = "public") +@Serializable +data class Product( + @Id var id: Long, + @Serializable(with = UuidSerialization::class) + val guid: UUID, + val name: String, + val description: String?, + val price: Int, + @Serializable(with = OffsetDateTimeSerialization::class) + @Column(name = "created_at") + val createdAt: OffsetDateTime, + @Serializable(with = OffsetDateTimeSerialization::class) + @Column(name = "updated_at") + val updatedAt: OffsetDateTime?, +) { + fun getPriceDouble(): Double = (price.toDouble() / 100).roundTo(2) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/demo/models/serializables/OffsetDateTimeSerialization.kt b/src/main/kotlin/com/example/demo/models/serializables/OffsetDateTimeSerialization.kt new file mode 100644 index 0000000..b7d8b4a --- /dev/null +++ b/src/main/kotlin/com/example/demo/models/serializables/OffsetDateTimeSerialization.kt @@ -0,0 +1,22 @@ +package com.example.demo.models.serializables + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter + +class OffsetDateTimeSerialization: KSerializer { + override val descriptor = PrimitiveSerialDescriptor("Time", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): OffsetDateTime { + return OffsetDateTime.parse(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: OffsetDateTime) { + encoder.encodeString(value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/demo/models/serializables/UuidSerialization.kt b/src/main/kotlin/com/example/demo/models/serializables/UuidSerialization.kt new file mode 100644 index 0000000..17e7c36 --- /dev/null +++ b/src/main/kotlin/com/example/demo/models/serializables/UuidSerialization.kt @@ -0,0 +1,21 @@ +package com.example.demo.models.serializables + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.util.* + +class UuidSerialization: KSerializer { + override val descriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): UUID { + return UUID.fromString(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: UUID) { + encoder.encodeString(value.toString()) + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/demo/provider/MockedShopProvider.kt b/src/main/kotlin/com/example/demo/provider/MockedShopProvider.kt index 75a5c0d..20dae95 100644 --- a/src/main/kotlin/com/example/demo/provider/MockedShopProvider.kt +++ b/src/main/kotlin/com/example/demo/provider/MockedShopProvider.kt @@ -1,28 +1,46 @@ package com.example.demo.provider import com.example.demo.models.* +import java.time.OffsetDateTime +import java.util.* class MockedShopProvider: ShopProvider { override fun getRandomShop(): Shop? { + val productOne = makeProduct(id = 1, name = "one", price = 11.2) + val productTwo = makeProduct(id = 2, name = "two", price = 13.2) + val productThree = makeProduct(id = 3, name = "three", price = 15.2) + val productFour = makeProduct(id = 4, name = "four", price = 14.2) + return Shop(name="shop", customers= listOf( Customer( name = "Foo-1", city = City(name= "Bar"), orders = listOf( - Order(products = listOf(Product(name = "one", price = 11.2)), isDelivered = false), - Order(products = listOf(Product(name = "two", price = 13.2)), isDelivered = false), - Order(products = listOf(Product(name = "three", price = 15.2)), isDelivered = true), + Order(products = listOf(productOne, productTwo), isDelivered = true), + Order(products = listOf(productThree), isDelivered = false), ) ), Customer( name = "Foo-2", city = City(name= "Bar"), orders = listOf( - Order(products = listOf(Product(name = "one", price = 12.2)), isDelivered = false), - Order(products = listOf(Product(name = "two", price = 13.2)), isDelivered = true), - Order(products = listOf(Product(name = "four", price = 14.2)), isDelivered = true), + Order(products = listOf(productOne), isDelivered = false), + Order(products = listOf(productTwo), isDelivered = true), + Order(products = listOf(productFour), isDelivered = true), ) ), )) } + + private fun makeProduct(id: Long, name: String, price: Double): Product { + return Product( + id = id, + guid = UUID.randomUUID(), + name = name, + description = null, + price = (price * 100).toInt(), + createdAt = OffsetDateTime.now(), + updatedAt = null, + ) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/demo/utils/Extensions.kt b/src/main/kotlin/com/example/demo/utils/Extensions.kt new file mode 100644 index 0000000..c47ed7f --- /dev/null +++ b/src/main/kotlin/com/example/demo/utils/Extensions.kt @@ -0,0 +1,9 @@ +package com.example.demo.utils + +import kotlin.math.pow +import kotlin.math.roundToInt + +fun Double.roundTo(numFractionDigits: Int): Double { + val factor = 10.0.pow(numFractionDigits.toDouble()) + return (this * factor).roundToInt() / factor +}