mirror of
https://github.com/Dannecron/spring-boot-demo.git
synced 2025-12-25 16:22:35 +03:00
trying to add order and order_product tables
with dtos, repositories and service
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("org.springframework.boot") version "3.2.10"
|
|
||||||
id("io.spring.dependency-management") version "1.1.6"
|
id("io.spring.dependency-management") version "1.1.6"
|
||||||
|
id("org.springframework.boot") version "3.2.10"
|
||||||
|
|
||||||
jacoco
|
jacoco
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.github.dannecron.demo.models
|
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
|
* Return the most expensive product among all delivered products
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,3 +1,31 @@
|
|||||||
package com.github.dannecron.demo.models
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.github.dannecron.demo.models
|
||||||
|
|
||||||
|
data class OrderLocal(val products: List<Product>, val isDelivered: Boolean)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,17 +16,17 @@ class MockedShopProvider: com.github.dannecron.demo.providers.ShopProvider {
|
|||||||
name = "Foo-1",
|
name = "Foo-1",
|
||||||
city = makeCity(id = 1, name = "Foo"),
|
city = makeCity(id = 1, name = "Foo"),
|
||||||
orders = listOf(
|
orders = listOf(
|
||||||
Order(products = listOf(productOne, productTwo), isDelivered = true),
|
OrderLocal(products = listOf(productOne, productTwo), isDelivered = true),
|
||||||
Order(products = listOf(productThree), isDelivered = false),
|
OrderLocal(products = listOf(productThree), isDelivered = false),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
CustomerLocal(
|
CustomerLocal(
|
||||||
name = "Foo-2",
|
name = "Foo-2",
|
||||||
city = makeCity(id = 2, name = "Bar"),
|
city = makeCity(id = 2, name = "Bar"),
|
||||||
orders = listOf(
|
orders = listOf(
|
||||||
Order(products = listOf(productOne), isDelivered = false),
|
OrderLocal(products = listOf(productOne), isDelivered = false),
|
||||||
Order(products = listOf(productTwo), isDelivered = true),
|
OrderLocal(products = listOf(productTwo), isDelivered = true),
|
||||||
Order(products = listOf(productFour), isDelivered = true),
|
OrderLocal(products = listOf(productFour), isDelivered = true),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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
|
||||||
|
);
|
||||||
@@ -28,8 +28,8 @@ class CustomerControllerTest(
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getCustomer_successWithCity() {
|
fun getCustomer_successWithCity() {
|
||||||
val customerId = 22.toLong()
|
val customerId = 22L
|
||||||
val cityId = 11.toLong()
|
val cityId = 11L
|
||||||
val customerGuid = UUID.randomUUID()
|
val customerGuid = UUID.randomUUID()
|
||||||
val customerExtended = CustomerExtended(
|
val customerExtended = CustomerExtended(
|
||||||
customer = Customer(
|
customer = Customer(
|
||||||
@@ -64,7 +64,7 @@ class CustomerControllerTest(
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getCustomer_successNoCity() {
|
fun getCustomer_successNoCity() {
|
||||||
val customerId = 22.toLong()
|
val customerId = 22L
|
||||||
val customerGuid = UUID.randomUUID()
|
val customerGuid = UUID.randomUUID()
|
||||||
val customerExtended = CustomerExtended(
|
val customerExtended = CustomerExtended(
|
||||||
customer = Customer(
|
customer = Customer(
|
||||||
|
|||||||
@@ -109,10 +109,10 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createProduct_success() {
|
fun createProduct_success() {
|
||||||
val productId = 13.toLong()
|
val productId = 13L
|
||||||
val name = "new-product"
|
val name = "new-product"
|
||||||
val description = null
|
val description = null
|
||||||
val price = 20000.toLong()
|
val price = 20000L
|
||||||
|
|
||||||
val reqBody = """{"name":"$name","description":null,"price":$price}"""
|
val reqBody = """{"name":"$name","description":null,"price":$price}"""
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createProduct_badRequest_noNameParam() {
|
fun createProduct_badRequest_noNameParam() {
|
||||||
val price = 20000.toLong()
|
val price = 20000L
|
||||||
|
|
||||||
val reqBody = """{"description":null,"price":$price}"""
|
val reqBody = """{"description":null,"price":$price}"""
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createProduct_badRequest_emptyName() {
|
fun createProduct_badRequest_emptyName() {
|
||||||
val price = 20000.toLong()
|
val price = 20000L
|
||||||
|
|
||||||
val reqBody = """{"name":"","description":null,"price":$price}"""
|
val reqBody = """{"name":"","description":null,"price":$price}"""
|
||||||
|
|
||||||
|
|||||||
@@ -32,18 +32,18 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
name = "cus-one",
|
name = "cus-one",
|
||||||
city = makeCity(id = 1, name = "city-one"),
|
city = makeCity(id = 1, name = "city-one"),
|
||||||
orders = listOf(
|
orders = listOf(
|
||||||
Order(products = listOf(productOne), isDelivered = false),
|
OrderLocal(products = listOf(productOne), isDelivered = false),
|
||||||
Order(products = listOf(productTwo), isDelivered = false),
|
OrderLocal(products = listOf(productTwo), isDelivered = false),
|
||||||
Order(products = listOf(productThree), isDelivered = true),
|
OrderLocal(products = listOf(productThree), isDelivered = true),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
CustomerLocal(
|
CustomerLocal(
|
||||||
name = "cus-two",
|
name = "cus-two",
|
||||||
city = makeCity(id = 2, name = "city-two"),
|
city = makeCity(id = 2, name = "city-two"),
|
||||||
orders = listOf(
|
orders = listOf(
|
||||||
Order(products = listOf(productOne), isDelivered = false),
|
OrderLocal(products = listOf(productOne), isDelivered = false),
|
||||||
Order(products = listOf(productTwo), isDelivered = true),
|
OrderLocal(products = listOf(productTwo), isDelivered = true),
|
||||||
Order(products = listOf(productFour), isDelivered = true),
|
OrderLocal(products = listOf(productFour), isDelivered = true),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ class ProductServiceImplDbTest: BaseDbTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun createFindDelete_success() {
|
fun createFindDelete_success() {
|
||||||
val name = "new-product-name"
|
val name = "new-product-name"
|
||||||
val price = 33333.toLong()
|
val price = 33333L
|
||||||
val description = "some-description"
|
val description = "some-description"
|
||||||
var product: Product? = null
|
var product: Product? = null
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user