trying to add order and order_product tables

with dtos, repositories and service
This commit is contained in:
Denis Savosin
2024-10-16 15:17:02 +07:00
parent 03d50b0be6
commit 08d3445ac4
16 changed files with 249 additions and 22 deletions

View File

@@ -1,6 +1,6 @@
plugins {
id("org.springframework.boot") version "3.2.10"
id("io.spring.dependency-management") version "1.1.6"
id("org.springframework.boot") version "3.2.10"
jacoco

View File

@@ -1,6 +1,6 @@
package com.github.dannecron.demo.models
data class CustomerLocal(val name: String, val city: City, val orders: List<Order>) {
data class CustomerLocal(val name: String, val city: City, val orders: List<OrderLocal>) {
/**
* Return the most expensive product among all delivered products
*/

View File

@@ -1,3 +1,31 @@
package com.github.dannecron.demo.models
data class Order(val products: List<Product>, val isDelivered: Boolean)
import com.github.dannecron.demo.services.serializables.OffsetDateTimeSerialization
import com.github.dannecron.demo.services.serializables.UuidSerialization
import kotlinx.serialization.Serializable
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Column
import org.springframework.data.relational.core.mapping.Table
import java.time.OffsetDateTime
import java.util.*
@Table(value = "order")
@Serializable
data class Order(
@Id
val id: Long?,
@Serializable(with = UuidSerialization::class)
val guid: UUID,
val customerId: Long,
@Serializable(with = OffsetDateTimeSerialization::class)
@Column(value = "delivered_at")
val deliveredAt: OffsetDateTime?,
@Serializable(with = OffsetDateTimeSerialization::class)
@Column(value = "created_at")
val createdAt: OffsetDateTime,
@Serializable(with = OffsetDateTimeSerialization::class)
@Column(value = "updated_at")
val updatedAt: OffsetDateTime?
) {
fun isDelivered(): Boolean = deliveredAt != null
}

View File

@@ -0,0 +1,3 @@
package com.github.dannecron.demo.models
data class OrderLocal(val products: List<Product>, val isDelivered: Boolean)

View File

@@ -0,0 +1,41 @@
package com.github.dannecron.demo.models
import com.github.dannecron.demo.services.serializables.OffsetDateTimeSerialization
import com.github.dannecron.demo.services.serializables.UuidSerialization
import kotlinx.serialization.Serializable
import org.springframework.data.annotation.Id
import org.springframework.data.annotation.Transient
import org.springframework.data.domain.Persistable
import org.springframework.data.relational.core.mapping.Column
import org.springframework.data.relational.core.mapping.Table
import java.time.OffsetDateTime
import java.util.*
@Table(value = "order_product")
@Serializable
data class OrderProduct(
@Id
@Serializable(with = UuidSerialization::class)
val guid: UUID,
@Column(value = "order_id")
val orderId: Long,
@Column(value = "product_id")
val productId: Long,
@Serializable(with = OffsetDateTimeSerialization::class)
@Column(value = "created_at")
val createdAt: OffsetDateTime,
@Serializable(with = OffsetDateTimeSerialization::class)
@Column(value = "updated_at")
val updatedAt: OffsetDateTime?,
): Persistable<UUID> {
@Transient
var isNewInstance: Boolean? = null
override fun getId(): UUID {
return guid
}
override fun isNew(): Boolean {
return isNewInstance ?: true
}
}

View File

@@ -16,17 +16,17 @@ class MockedShopProvider: com.github.dannecron.demo.providers.ShopProvider {
name = "Foo-1",
city = makeCity(id = 1, name = "Foo"),
orders = listOf(
Order(products = listOf(productOne, productTwo), isDelivered = true),
Order(products = listOf(productThree), isDelivered = false),
OrderLocal(products = listOf(productOne, productTwo), isDelivered = true),
OrderLocal(products = listOf(productThree), isDelivered = false),
)
),
CustomerLocal(
name = "Foo-2",
city = makeCity(id = 2, name = "Bar"),
orders = listOf(
Order(products = listOf(productOne), isDelivered = false),
Order(products = listOf(productTwo), isDelivered = true),
Order(products = listOf(productFour), isDelivered = true),
OrderLocal(products = listOf(productOne), isDelivered = false),
OrderLocal(products = listOf(productTwo), isDelivered = true),
OrderLocal(products = listOf(productFour), isDelivered = true),
)
),
))

View File

@@ -0,0 +1,7 @@
package com.github.dannecron.demo.providers
import com.github.dannecron.demo.models.OrderProduct
import org.springframework.data.repository.CrudRepository
import java.util.*
interface OrderProductRepository: CrudRepository<OrderProduct, UUID>

View File

@@ -0,0 +1,8 @@
package com.github.dannecron.demo.providers
import com.github.dannecron.demo.models.Order
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
@Repository
interface OrderRepository: CrudRepository<Order, Long>

View File

@@ -0,0 +1,42 @@
package com.github.dannecron.demo.services.database.order
import com.github.dannecron.demo.models.Customer
import com.github.dannecron.demo.models.Order
import com.github.dannecron.demo.models.OrderProduct
import com.github.dannecron.demo.models.Product
import com.github.dannecron.demo.providers.OrderProductRepository
import com.github.dannecron.demo.providers.OrderRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.OffsetDateTime
import java.util.*
@Service
class OrderServiceImpl(
@Autowired val orderRepository: OrderRepository,
@Autowired val orderProductRepository: OrderProductRepository,
) {
@Transactional
fun createOrder(customer: Customer, products: Set<Product>): Order {
val order = Order(
id = null,
guid = UUID.randomUUID(),
customerId = customer.id!!,
deliveredAt = null,
createdAt = OffsetDateTime.now(),
updatedAt = null,
)
return orderRepository.save(order).also {
savedOrder -> products.toList().forEach {
product -> orderProductRepository.save(OrderProduct(
guid = UUID.randomUUID(),
orderId = savedOrder.id!!,
productId = product.id!!,
createdAt = OffsetDateTime.now(),
updatedAt = null
))
}
}
}
}

View File

@@ -0,0 +1,14 @@
create table "order" (
id bigserial primary key,
guid uuid not null,
customer_id bigint not null,
delivered_at timestamptz,
created_at timestamptz not null,
updated_at timestamptz,
CONSTRAINT order_customer_foreign
FOREIGN KEY(customer_id)
REFERENCES customer(id)
ON DELETE CASCADE
);
create unique index order_guid_idx ON "order" (guid);

View File

@@ -0,0 +1,15 @@
create table order_product (
guid uuid primary key,
order_id bigint not null,
product_id bigint not null,
created_at timestamptz not null,
updated_at timestamptz,
CONSTRAINT order_product_order_foreign
FOREIGN KEY(order_id)
REFERENCES "order"(id)
ON DELETE CASCADE,
CONSTRAINT order_product_product_foreign
FOREIGN KEY(product_id)
REFERENCES product(id)
ON DELETE CASCADE
);

View File

@@ -28,8 +28,8 @@ class CustomerControllerTest(
@Test
fun getCustomer_successWithCity() {
val customerId = 22.toLong()
val cityId = 11.toLong()
val customerId = 22L
val cityId = 11L
val customerGuid = UUID.randomUUID()
val customerExtended = CustomerExtended(
customer = Customer(
@@ -64,7 +64,7 @@ class CustomerControllerTest(
@Test
fun getCustomer_successNoCity() {
val customerId = 22.toLong()
val customerId = 22L
val customerGuid = UUID.randomUUID()
val customerExtended = CustomerExtended(
customer = Customer(

View File

@@ -109,10 +109,10 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
@Test
fun createProduct_success() {
val productId = 13.toLong()
val productId = 13L
val name = "new-product"
val description = null
val price = 20000.toLong()
val price = 20000L
val reqBody = """{"name":"$name","description":null,"price":$price}"""
@@ -142,7 +142,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
@Test
fun createProduct_badRequest_noNameParam() {
val price = 20000.toLong()
val price = 20000L
val reqBody = """{"description":null,"price":$price}"""
@@ -160,7 +160,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
@Test
fun createProduct_badRequest_emptyName() {
val price = 20000.toLong()
val price = 20000L
val reqBody = """{"name":"","description":null,"price":$price}"""

View File

@@ -32,18 +32,18 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
name = "cus-one",
city = makeCity(id = 1, name = "city-one"),
orders = listOf(
Order(products = listOf(productOne), isDelivered = false),
Order(products = listOf(productTwo), isDelivered = false),
Order(products = listOf(productThree), isDelivered = true),
OrderLocal(products = listOf(productOne), isDelivered = false),
OrderLocal(products = listOf(productTwo), isDelivered = false),
OrderLocal(products = listOf(productThree), isDelivered = true),
)
),
CustomerLocal(
name = "cus-two",
city = makeCity(id = 2, name = "city-two"),
orders = listOf(
Order(products = listOf(productOne), isDelivered = false),
Order(products = listOf(productTwo), isDelivered = true),
Order(products = listOf(productFour), isDelivered = true),
OrderLocal(products = listOf(productOne), isDelivered = false),
OrderLocal(products = listOf(productTwo), isDelivered = true),
OrderLocal(products = listOf(productFour), isDelivered = true),
)
),
))

View File

@@ -0,0 +1,69 @@
package com.github.dannecron.demo.services.database.order
import com.github.dannecron.demo.BaseDbTest
import com.github.dannecron.demo.models.Customer
import com.github.dannecron.demo.models.Order
import com.github.dannecron.demo.models.Product
import com.github.dannecron.demo.providers.CustomerRepository
import com.github.dannecron.demo.providers.OrderRepository
import com.github.dannecron.demo.providers.ProductRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import java.time.OffsetDateTime
import java.util.*
import kotlin.test.Test
import kotlin.test.assertNotNull
@ContextConfiguration(classes = [OrderServiceImpl::class])
class OrderServiceImplTest: BaseDbTest() {
@Autowired
private lateinit var orderRepository: OrderRepository
@Autowired
private lateinit var productRepository: ProductRepository
@Autowired
private lateinit var customerRepository: CustomerRepository
@Autowired
private lateinit var orderServiceImpl: OrderServiceImpl
@Test
fun create_success() {
var productOne: Product? = null
var productTwo: Product? = null
var customer: Customer? = null
var order: Order? = null
try {
productOne = makeProduct().let { productRepository.save(it) }
productTwo = makeProduct().let { productRepository.save(it) }
customer = makeCustomer().let { customerRepository.save(it) }
order = orderServiceImpl.createOrder(customer, setOf(productOne, productTwo))
assertNotNull(order.id)
} finally {
order?.id?.also { orderRepository.deleteById(it) }
customer?.id?.also { customerRepository.deleteById(it) }
productOne?.id?.also { productRepository.deleteById(it) }
productTwo?.id?.also { productRepository.deleteById(it) }
}
}
private fun makeProduct(): Product = Product(
id = null,
guid = UUID.randomUUID(),
name = "name" + UUID.randomUUID(),
description = null,
price = 10000,
createdAt = OffsetDateTime.now(),
updatedAt = null,
deletedAt = null,
)
private fun makeCustomer(): Customer = Customer(
id = null,
guid = UUID.randomUUID(),
name = "client",
cityId = null,
createdAt =OffsetDateTime.now(),
updatedAt = null,
)
}

View File

@@ -29,7 +29,7 @@ class ProductServiceImplDbTest: BaseDbTest() {
@Test
fun createFindDelete_success() {
val name = "new-product-name"
val price = 33333.toLong()
val price = 33333L
val description = "some-description"
var product: Product? = null