mirror of
https://github.com/Dannecron/spring-boot-demo.git
synced 2025-12-25 16:22:35 +03:00
add new api methods to product controller
This commit is contained in:
16
doc/examples/api/product.md
Normal file
16
doc/examples/api/product.md
Normal file
@@ -0,0 +1,16 @@
|
||||
```shell
|
||||
curl --request GET \
|
||||
--url 'http://localhost:8080/api/product/179cffdc-90f8-4627-985d-3d9c88dff5d7'
|
||||
```
|
||||
|
||||
```shell
|
||||
curl --request POST \
|
||||
--url http://localhost:8080/api/product \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"product-tree","description":"some other product","price":30000}'
|
||||
```
|
||||
|
||||
```shell
|
||||
curl --request DELETE \
|
||||
--url 'http://localhost:8080/api/product/179cffdc-90f8-4627-985d-3d9c88dff5d7'
|
||||
```
|
||||
@@ -1,16 +1,23 @@
|
||||
package com.example.demo
|
||||
|
||||
import com.example.demo.provider.MockedShopProvider
|
||||
import com.example.demo.provider.ProductRepository
|
||||
import com.example.demo.provider.ShopProvider
|
||||
import com.example.demo.services.ProductService
|
||||
import com.example.demo.services.ProductServiceImpl
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
|
||||
|
||||
@Configuration
|
||||
@EnableJpaRepositories(basePackages = ["com.example.demo.providers"])
|
||||
class AppConfig {
|
||||
@Bean
|
||||
fun shopProvider(): ShopProvider{
|
||||
return MockedShopProvider()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun productService(@Autowired productRepository: ProductRepository): ProductService {
|
||||
return ProductServiceImpl(productRepository = productRepository)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,56 @@
|
||||
package com.example.demo.controllers
|
||||
|
||||
import com.example.demo.exceptions.NotFoundException
|
||||
import com.example.demo.models.Product
|
||||
import com.example.demo.provider.ProductRepository
|
||||
import com.example.demo.exceptions.UnprocessableException
|
||||
import com.example.demo.requests.CreateProductRequest
|
||||
import com.example.demo.responses.makeOkResponse
|
||||
import com.example.demo.services.ProductService
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
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.*
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.*
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = ["/api/product"])
|
||||
@RequestMapping(value = ["/api/product"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
class ProductController(
|
||||
@Autowired val productRepository: ProductRepository
|
||||
val productService: ProductService
|
||||
) {
|
||||
@GetMapping(value = ["{guid}"], produces = ["application/json"])
|
||||
@GetMapping("/{guid}")
|
||||
@ResponseBody
|
||||
@Throws(NotFoundException::class)
|
||||
fun getProduct(
|
||||
@PathVariable guid: UUID
|
||||
): String {
|
||||
val product = productRepository.findByGuid(guid = guid) ?: throw NotFoundException()
|
||||
val product = productService.findByGuid(guid = guid) ?: throw NotFoundException()
|
||||
|
||||
return Json.encodeToJsonElement(value = product).toString()
|
||||
}
|
||||
|
||||
@PostMapping(value = [""], consumes = ["application/json"], produces = ["application/json"])
|
||||
@PostMapping(value = ["/"], consumes = [MediaType.APPLICATION_JSON_VALUE])
|
||||
@ResponseBody
|
||||
fun createProduct(
|
||||
@RequestBody product: CreateProductRequest
|
||||
): String {
|
||||
val productModel = Product(
|
||||
id = null,
|
||||
guid = UUID.randomUUID(),
|
||||
name = product.name,
|
||||
description = product.description,
|
||||
price = product.price,
|
||||
createdAt = OffsetDateTime.now(),
|
||||
updatedAt = null,
|
||||
val saved = productService.create(
|
||||
product.name,
|
||||
product.price,
|
||||
product.description,
|
||||
)
|
||||
|
||||
val saved = productRepository.save(productModel)
|
||||
|
||||
return Json.encodeToJsonElement(value = saved).toString()
|
||||
}
|
||||
|
||||
// todo delete with soft-delete
|
||||
@DeleteMapping("/{guid}")
|
||||
@ResponseBody
|
||||
@Throws(NotFoundException::class, UnprocessableException::class)
|
||||
fun deleteProduct(
|
||||
@PathVariable guid: UUID,
|
||||
): ResponseEntity<Any> {
|
||||
productService.delete(guid)
|
||||
|
||||
return ResponseEntity(makeOkResponse(), HttpStatus.OK)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.example.demo.exceptions
|
||||
|
||||
import com.example.demo.responses.makeNotFound
|
||||
import com.example.demo.responses.makeNotFoundResponse
|
||||
import com.example.demo.responses.makeUnprocessableResponse
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice
|
||||
@@ -9,5 +10,11 @@ import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
@ControllerAdvice
|
||||
class ExceptionHandler {
|
||||
@ExceptionHandler(NotFoundException::class)
|
||||
fun handleNotFound(): ResponseEntity<Any> = ResponseEntity(makeNotFound(), HttpStatus.NOT_FOUND)
|
||||
fun handleNotFound(): ResponseEntity<Any> = ResponseEntity(makeNotFoundResponse(), HttpStatus.NOT_FOUND)
|
||||
|
||||
@ExceptionHandler(UnprocessableException::class)
|
||||
fun handleUnprocessable(exception: UnprocessableException): ResponseEntity<Any> = ResponseEntity(
|
||||
makeUnprocessableResponse(exception.message),
|
||||
HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
)
|
||||
}
|
||||
@@ -41,6 +41,7 @@ class MockedShopProvider: ShopProvider {
|
||||
price = (price * 100).toLong(),
|
||||
createdAt = OffsetDateTime.now(),
|
||||
updatedAt = null,
|
||||
deletedAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.example.demo.responses
|
||||
|
||||
class BaseResponse(val status: ResponseStatus)
|
||||
open class BaseResponse(val status: ResponseStatus)
|
||||
|
||||
fun makeNotFound(): BaseResponse = BaseResponse(status = ResponseStatus.NOT_FOUND)
|
||||
fun makeOkResponse(): BaseResponse = BaseResponse(status = ResponseStatus.OK)
|
||||
|
||||
fun makeNotFoundResponse(): BaseResponse = BaseResponse(status = ResponseStatus.NOT_FOUND)
|
||||
|
||||
@@ -3,5 +3,7 @@ package com.example.demo.responses
|
||||
import com.fasterxml.jackson.annotation.JsonValue
|
||||
|
||||
enum class ResponseStatus(@JsonValue val status: String) {
|
||||
NOT_FOUND("not found");
|
||||
OK("ok"),
|
||||
NOT_FOUND("not found"),
|
||||
UNPROCESSABLE("unprocessable");
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.example.demo.responses
|
||||
|
||||
class UnprocessableResponse(
|
||||
val cause: String,
|
||||
): BaseResponse(status = ResponseStatus.UNPROCESSABLE)
|
||||
|
||||
fun makeUnprocessableResponse(cause: String): UnprocessableResponse = UnprocessableResponse(cause)
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.example.demo.controllers
|
||||
|
||||
import com.example.demo.models.Product
|
||||
import com.example.demo.provider.ProductRepository
|
||||
import com.example.demo.responses.ResponseStatus
|
||||
import com.example.demo.services.ProductService
|
||||
import org.hamcrest.Matchers.nullValue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.doReturn
|
||||
@@ -20,7 +20,7 @@ import java.util.*
|
||||
@WebMvcTest(ProductController::class)
|
||||
class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
@MockBean
|
||||
private lateinit var productRepository: ProductRepository
|
||||
private lateinit var productService: ProductService
|
||||
|
||||
@Test
|
||||
fun getProduct_success() {
|
||||
@@ -34,9 +34,10 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
price = 11130,
|
||||
createdAt = now,
|
||||
updatedAt = null,
|
||||
deletedAt = null,
|
||||
)
|
||||
|
||||
whenever(productRepository.findByGuid(
|
||||
whenever(productService.findByGuid(
|
||||
eq(guid),
|
||||
)) doReturn product
|
||||
|
||||
@@ -54,7 +55,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
fun getProduct_notFound() {
|
||||
val guid = UUID.randomUUID()
|
||||
|
||||
whenever(productRepository.findByGuid(
|
||||
whenever(productService.findByGuid(
|
||||
eq(guid),
|
||||
)) doReturn null
|
||||
|
||||
|
||||
@@ -81,9 +81,10 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
guid = UUID.randomUUID(),
|
||||
name = name,
|
||||
description = null,
|
||||
price = (price * 100).toInt(),
|
||||
price = (price * 100).toLong(),
|
||||
createdAt = OffsetDateTime.now(),
|
||||
updatedAt = null,
|
||||
deletedAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user