mirror of
https://github.com/Dannecron/spring-boot-demo.git
synced 2025-12-25 16:22:35 +03:00
add customer controller with find by guid method
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
package com.example.demo.http.controllers
|
||||||
|
|
||||||
|
import com.example.demo.http.exceptions.NotFoundException
|
||||||
|
import com.example.demo.services.database.customer.CustomerService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(value = ["/api/customer"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
|
class CustomerController(
|
||||||
|
@Autowired
|
||||||
|
private val customerService: CustomerService,
|
||||||
|
) {
|
||||||
|
@GetMapping("/{guid}")
|
||||||
|
@Throws(NotFoundException::class)
|
||||||
|
fun getCustomer(
|
||||||
|
@PathVariable guid: UUID,
|
||||||
|
): ResponseEntity<Any> {
|
||||||
|
val customer = customerService.findByGuid(guid) ?: throw NotFoundException()
|
||||||
|
|
||||||
|
return ResponseEntity(customer, HttpStatus.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import java.time.OffsetDateTime
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Table("customer")
|
@Table("customer")
|
||||||
|
@Serializable
|
||||||
data class Customer(
|
data class Customer(
|
||||||
@Id
|
@Id
|
||||||
val id: Long?,
|
val id: Long?,
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.example.demo.models
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CustomerExtended(
|
||||||
|
val customer: Customer,
|
||||||
|
val city: City?,
|
||||||
|
)
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package com.example.demo.services.database.customer
|
package com.example.demo.services.database.customer
|
||||||
|
|
||||||
import com.example.demo.models.Customer
|
import com.example.demo.models.Customer
|
||||||
|
import com.example.demo.models.CustomerExtended
|
||||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface CustomerService {
|
interface CustomerService {
|
||||||
fun findByGuid(guid: UUID): Customer?
|
fun findByGuid(guid: UUID): CustomerExtended?
|
||||||
|
|
||||||
@Throws(CityNotFoundException::class)
|
@Throws(CityNotFoundException::class)
|
||||||
fun create(name: String, cityGuid: UUID?): Customer
|
fun create(name: String, cityGuid: UUID?): Customer
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.demo.services.database.customer
|
package com.example.demo.services.database.customer
|
||||||
|
|
||||||
import com.example.demo.models.Customer
|
import com.example.demo.models.Customer
|
||||||
|
import com.example.demo.models.CustomerExtended
|
||||||
import com.example.demo.providers.CityRepository
|
import com.example.demo.providers.CityRepository
|
||||||
import com.example.demo.providers.CustomerRepository
|
import com.example.demo.providers.CustomerRepository
|
||||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||||
@@ -11,7 +12,17 @@ class CustomerServiceImpl(
|
|||||||
private val customerRepository: CustomerRepository,
|
private val customerRepository: CustomerRepository,
|
||||||
private val cityRepository: CityRepository
|
private val cityRepository: CityRepository
|
||||||
): CustomerService {
|
): CustomerService {
|
||||||
override fun findByGuid(guid: UUID): Customer? = customerRepository.findByGuid(guid)
|
override fun findByGuid(guid: UUID): CustomerExtended? {
|
||||||
|
val customer = customerRepository.findByGuid(guid) ?: return null
|
||||||
|
|
||||||
|
if (customer.cityId == null) {
|
||||||
|
return CustomerExtended(customer, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val city = cityRepository.findById(customer.cityId)
|
||||||
|
|
||||||
|
return CustomerExtended(customer, city.orElse(null))
|
||||||
|
}
|
||||||
|
|
||||||
override fun create(name: String, cityGuid: UUID?): Customer {
|
override fun create(name: String, cityGuid: UUID?): Customer {
|
||||||
val cityId: Long? = cityGuid?.let {
|
val cityId: Long? = cityGuid?.let {
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.example.demo.http.controllers
|
||||||
|
|
||||||
|
import com.example.demo.BaseUnitTest
|
||||||
|
import com.example.demo.http.responses.ResponseStatus
|
||||||
|
import com.example.demo.models.City
|
||||||
|
import com.example.demo.models.Customer
|
||||||
|
import com.example.demo.models.CustomerExtended
|
||||||
|
import com.example.demo.services.database.customer.CustomerService
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.eq
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.get
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@WebMvcTest(CustomerController::class)
|
||||||
|
class CustomerControllerTest(
|
||||||
|
@Autowired val mockMvc: MockMvc,
|
||||||
|
): BaseUnitTest() {
|
||||||
|
@MockBean
|
||||||
|
private lateinit var customerService: CustomerService
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getCustomer_successWithCity() {
|
||||||
|
val customerId = 22.toLong()
|
||||||
|
val cityId = 11.toLong()
|
||||||
|
val customerGuid = UUID.randomUUID()
|
||||||
|
val customerExtended = CustomerExtended(
|
||||||
|
customer = Customer(
|
||||||
|
id = customerId,
|
||||||
|
guid = customerGuid,
|
||||||
|
name = "Test Person",
|
||||||
|
cityId = cityId,
|
||||||
|
createdAt = OffsetDateTime.now().minusHours(1),
|
||||||
|
updatedAt = OffsetDateTime.now(),
|
||||||
|
),
|
||||||
|
city = City(
|
||||||
|
id = cityId,
|
||||||
|
guid = UUID.randomUUID(),
|
||||||
|
name = "Test City",
|
||||||
|
createdAt = OffsetDateTime.now().minusWeeks(1),
|
||||||
|
updatedAt = null,
|
||||||
|
deletedAt = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
whenever(customerService.findByGuid(
|
||||||
|
eq(customerGuid),
|
||||||
|
)) doReturn customerExtended
|
||||||
|
|
||||||
|
mockMvc.get("/api/customer/$customerGuid")
|
||||||
|
.andExpect { status { isOk() } }
|
||||||
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
|
.andExpect { jsonPath("\$.customer.id") { value(customerId) } }
|
||||||
|
.andExpect { jsonPath("\$.customer.cityId") { value(cityId) } }
|
||||||
|
.andExpect { jsonPath("\$.city.id") { value(cityId) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getCustomer_successNoCity() {
|
||||||
|
val customerId = 22.toLong()
|
||||||
|
val customerGuid = UUID.randomUUID()
|
||||||
|
val customerExtended = CustomerExtended(
|
||||||
|
customer = Customer(
|
||||||
|
id = customerId,
|
||||||
|
guid = customerGuid,
|
||||||
|
name = "Test Person",
|
||||||
|
cityId = null,
|
||||||
|
createdAt = OffsetDateTime.now().minusHours(1),
|
||||||
|
updatedAt = OffsetDateTime.now(),
|
||||||
|
),
|
||||||
|
city = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
whenever(customerService.findByGuid(
|
||||||
|
eq(customerGuid),
|
||||||
|
)) doReturn customerExtended
|
||||||
|
|
||||||
|
mockMvc.get("/api/customer/$customerGuid")
|
||||||
|
.andExpect { status { isOk() } }
|
||||||
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
|
.andExpect { jsonPath("\$.customer.id") { value(customerId) } }
|
||||||
|
.andExpect { jsonPath("\$.customer.cityId") { value(null) } }
|
||||||
|
.andExpect { jsonPath("\$.city") { value(null) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getCustomer_successNotFound() {
|
||||||
|
val customerGuid = UUID.randomUUID()
|
||||||
|
|
||||||
|
whenever(customerService.findByGuid(
|
||||||
|
eq(customerGuid),
|
||||||
|
)) doReturn null
|
||||||
|
|
||||||
|
mockMvc.get("/api/customer/$customerGuid")
|
||||||
|
.andExpect { status { isNotFound() } }
|
||||||
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
|
.andExpect { jsonPath("\$.status") { value(ResponseStatus.NOT_FOUND.status) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,26 +5,24 @@ import org.hamcrest.core.StringContains
|
|||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
import org.springframework.test.web.servlet.get
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
|
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
@WebMvcTest(GreetingController::class)
|
@WebMvcTest(GreetingController::class)
|
||||||
class GreetingControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
class GreetingControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
||||||
@Test
|
@Test
|
||||||
fun greetings_shouldSeeGreetingMessage() {
|
fun greetings_shouldSeeGreetingMessage() {
|
||||||
mockMvc.perform(get("/greeting"))
|
mockMvc.get("/greeting")
|
||||||
.andExpect(status().isOk)
|
.andExpect { status { isOk() } }
|
||||||
.andExpect(content().contentType("text/plain;charset=UTF-8"))
|
.andExpect { content { contentType("text/plain;charset=UTF-8") } }
|
||||||
.andExpect(content().string("Hello World!"))
|
.andExpect { content { string("Hello World!") } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun exampleHtml_shouldSeeRenderedHtml() {
|
fun exampleHtml_shouldSeeRenderedHtml() {
|
||||||
mockMvc.perform(get("/example/html"))
|
mockMvc.get("/example/html")
|
||||||
.andExpect(status().isOk)
|
.andExpect { status { isOk() } }
|
||||||
.andExpect(content().contentType("text/html;charset=UTF-8"))
|
.andExpect { content { contentType("text/html;charset=UTF-8") } }
|
||||||
.andExpect(content().string(StringContains("Product")))
|
.andExpect { content { string(StringContains("Product")) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
)) doReturn product
|
)) doReturn product
|
||||||
|
|
||||||
mockMvc.get("/api/product/$guid")
|
mockMvc.get("/api/product/$guid")
|
||||||
.andExpect { status { status { isOk() } } }
|
.andExpect { status { isOk() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.id") { value(product.id.toString()) } }
|
.andExpect { jsonPath("\$.id") { value(product.id.toString()) } }
|
||||||
.andExpect { jsonPath("\$.guid") { value(guid.toString()) } }
|
.andExpect { jsonPath("\$.guid") { value(guid.toString()) } }
|
||||||
@@ -70,7 +70,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
)) doReturn null
|
)) doReturn null
|
||||||
|
|
||||||
mockMvc.get("/api/product/$guid")
|
mockMvc.get("/api/product/$guid")
|
||||||
.andExpect { status { status { isNotFound() } } }
|
.andExpect { status { isNotFound() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.status") { value(ResponseStatus.NOT_FOUND.status) } }
|
.andExpect { jsonPath("\$.status") { value(ResponseStatus.NOT_FOUND.status) } }
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
)))
|
)))
|
||||||
|
|
||||||
mockMvc.get("/api/product?page=1&size=2&sort=createdAt,desc")
|
mockMvc.get("/api/product?page=1&size=2&sort=createdAt,desc")
|
||||||
.andExpect { status { status { isOk() } } }
|
.andExpect { status { isOk() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.meta.total") { value(1) } }
|
.andExpect { jsonPath("\$.meta.total") { value(1) } }
|
||||||
.andExpect { jsonPath("\$.meta.pages") { value(1) } }
|
.andExpect { jsonPath("\$.meta.pages") { value(1) } }
|
||||||
@@ -135,7 +135,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
contentType = MediaType.APPLICATION_JSON
|
contentType = MediaType.APPLICATION_JSON
|
||||||
content = reqBody
|
content = reqBody
|
||||||
}
|
}
|
||||||
.andExpect { status { status { isCreated() } } }
|
.andExpect { status { isCreated() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.id") { value(productId) } }
|
.andExpect { jsonPath("\$.id") { value(productId) } }
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
contentType = MediaType.APPLICATION_JSON
|
contentType = MediaType.APPLICATION_JSON
|
||||||
content = reqBody
|
content = reqBody
|
||||||
}
|
}
|
||||||
.andExpect { status { status { isBadRequest() } } }
|
.andExpect { status { isBadRequest() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.status") { value(ResponseStatus.BAD_REQUEST.status) } }
|
.andExpect { jsonPath("\$.status") { value(ResponseStatus.BAD_REQUEST.status) } }
|
||||||
.andExpect { jsonPath("\$.cause") { contains("name") } }
|
.andExpect { jsonPath("\$.cause") { contains("name") } }
|
||||||
@@ -168,7 +168,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
contentType = MediaType.APPLICATION_JSON
|
contentType = MediaType.APPLICATION_JSON
|
||||||
content = reqBody
|
content = reqBody
|
||||||
}
|
}
|
||||||
.andExpect { status { status { isUnprocessableEntity() } } }
|
.andExpect { status { isUnprocessableEntity() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.status") { value(ResponseStatus.UNPROCESSABLE.status) } }
|
.andExpect { jsonPath("\$.status") { value(ResponseStatus.UNPROCESSABLE.status) } }
|
||||||
.andExpect { jsonPath("\$.cause") { value(MethodArgumentNotValidException::class.qualifiedName) } }
|
.andExpect { jsonPath("\$.cause") { value(MethodArgumentNotValidException::class.qualifiedName) } }
|
||||||
@@ -194,8 +194,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
mockMvc.delete("/api/product/${guid}")
|
mockMvc.delete("/api/product/${guid}")
|
||||||
.andExpect { status { status { isOk() } } }
|
.andExpect { status { isOk() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { jsonPath("\$.status") { value(ResponseStatus.OK.status) } }
|
.andExpect { jsonPath("\$.status") { value(ResponseStatus.OK.status) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
|||||||
) doReturn null
|
) doReturn null
|
||||||
|
|
||||||
mockMvc.get("/shop/common-info")
|
mockMvc.get("/shop/common-info")
|
||||||
.andExpect { status { status { isNotFound() } } }
|
.andExpect { status { isNotFound() } }
|
||||||
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
|
||||||
.andExpect { content { json("""{"status":"not found"}""") } }
|
.andExpect { content { json("""{"status":"not found"}""") } }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,22 +41,23 @@ class CustomerServiceImplDbTest: BaseDbTest() {
|
|||||||
try {
|
try {
|
||||||
city = cityRepository.save(city)
|
city = cityRepository.save(city)
|
||||||
|
|
||||||
customerServiceImpl.create(nameOne, city.guid).let {
|
customerServiceImpl.create(nameTwo, null).let {
|
||||||
customerIds += it.id ?: fail("customerWithCity id is null")
|
customerIds += it.id ?: fail("customerWithNoCity id is null")
|
||||||
assertEquals(city.id, it.cityId)
|
assertNull(it.cityId)
|
||||||
assertNotNull(it.createdAt)
|
assertNotNull(it.createdAt)
|
||||||
assertNull(it.updatedAt)
|
assertNull(it.updatedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
val customerWithNoCity = customerServiceImpl.create(nameTwo, null)
|
val customerWithCity = customerServiceImpl.create(nameOne, city.guid)
|
||||||
customerIds += customerWithNoCity.id ?: fail("customerWithNoCity id is null")
|
customerIds += customerWithCity.id ?: fail("customerWithCity id is null")
|
||||||
assertNull(customerWithNoCity.cityId)
|
assertEquals(city.id, customerWithCity.cityId)
|
||||||
assertNotNull(customerWithNoCity.createdAt)
|
assertNotNull(customerWithCity.createdAt)
|
||||||
assertNull(customerWithNoCity.updatedAt)
|
assertNull(customerWithCity.updatedAt)
|
||||||
|
|
||||||
val existedCustomer = customerServiceImpl.findByGuid(customerWithNoCity.guid)
|
val existedCustomer = customerServiceImpl.findByGuid(customerWithCity.guid)
|
||||||
assertNotNull(existedCustomer)
|
assertNotNull(existedCustomer)
|
||||||
assertEquals(customerWithNoCity.id, existedCustomer.id)
|
assertEquals(customerWithCity.id, existedCustomer.customer.id)
|
||||||
|
assertEquals(city.id, existedCustomer.city?.id)
|
||||||
|
|
||||||
assertThrows<CityNotFoundException> {
|
assertThrows<CityNotFoundException> {
|
||||||
customerServiceImpl.create(nameThree, UUID.randomUUID())
|
customerServiceImpl.create(nameThree, UUID.randomUUID())
|
||||||
|
|||||||
Reference in New Issue
Block a user