add new api methods to product controller

This commit is contained in:
Denis Savosin
2024-09-30 12:41:29 +07:00
parent f12839a15f
commit 27595e08dc
11 changed files with 83 additions and 33 deletions

View 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'
```

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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,
)
}

View File

@@ -41,6 +41,7 @@ class MockedShopProvider: ShopProvider {
price = (price * 100).toLong(),
createdAt = OffsetDateTime.now(),
updatedAt = null,
deletedAt = null,
)
}
}

View File

@@ -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)

View File

@@ -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");
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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,
)
}
}