Skip to content

Commit

Permalink
Add rest endpoints (#12)
Browse files Browse the repository at this point in the history
* Created empty files for individual layers

* Added mongodb configs

* Added cart model

* Added repository

* Updated repository

* Created service layer

* Minor change

* Added rest endpoints for application

* Added existByUserId function

* Update service layer

* Updated controller layer

* Added custom page request to support pageable

* Changes done to support pageable

* Remove pageable from get cart

* Remove fixture

* Add unit tests (#16)

* Created test file and added add product to cart unit test

* Fixed typo

* Added one more unit test for adding product in cart

* FIXED detekt build fail

* Remove unuse param

* Added more unit test

* Remove unused function

* Minor change

* Added more unit tests

* FIXED: Logic of empty cart

* Added more unit tests
  • Loading branch information
Harsh3305 authored Mar 11, 2023
1 parent 2e44837 commit a695638
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.backendcart.controller

import com.example.backendcart.model.Cart
import com.example.backendcart.service.CartService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.server.reactive.ServerHttpResponse
import org.springframework.web.bind.annotation.*
import java.util.*

@RestController
@RequestMapping("/cart")
class CartController (
@Autowired
private val cartService: CartService
)
{
@PostMapping
fun addProductInCart(@RequestBody cart: Cart, response: ServerHttpResponse) =
cartService.addProductToCart(cart, response)
@GetMapping("/{userId}/{productId}")
fun getProductQuantityInCart(
@PathVariable productId: String,
@PathVariable userId: String
) =
cartService.getProductQuantityInCart(userId, productId)
@GetMapping("/{userId}")
fun getUserCart(@PathVariable userId: String) =
cartService.getUserCart(userId)
@PutMapping
fun updateProductQuantity(
@RequestBody cart: Cart,
response: ServerHttpResponse
) =
cartService.updateProductQuantity(cart, response)
@DeleteMapping("/{userId}/{productId}")
fun deleteProductFromCart(
@PathVariable productId: String,
@PathVariable userId: String,
response: ServerHttpResponse
) =
cartService.deleteProductFromCart(
userId,
productId,
response
)
@DeleteMapping("/{userId}")
fun emptyCart(
@PathVariable userId: String,
response: ServerHttpResponse
) =
cartService.emptyUserCart(
userId,
response
)
}
13 changes: 13 additions & 0 deletions src/main/kotlin/com/example/backendcart/model/Cart.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.backendcart.model

import org.springframework.data.mongodb.core.index.CompoundIndex
import org.springframework.data.mongodb.core.mapping.Document

@CompoundIndex(name = "cart_idx", def = "{'userId': 1, 'productId': 1}",
unique = true)
@Document("Cart")
data class Cart (
val userId: String,
val productId: String,
val quantity: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.backendcart.repository

import com.example.backendcart.model.Cart
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

@Repository
interface CartRepository : ReactiveMongoRepository<Cart, String> {
fun findByUserId(userId: String): Flux<Cart>
fun findByUserIdAndProductId(userId: String, productId: String): Mono<Cart>
fun existsByUserIdAndProductId(userId: String, productId: String): Mono<Boolean>
fun existsByUserId(userId: String): Mono<Boolean>
fun deleteByUserIdAndProductId(userId: String, productId: String): Mono<Void>
fun deleteByUserId(userId: String): Flux<Void>
}
77 changes: 77 additions & 0 deletions src/main/kotlin/com/example/backendcart/service/CartService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.example.backendcart.service

import com.example.backendcart.model.Cart
import com.example.backendcart.repository.CartRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.server.reactive.ServerHttpResponse
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono

@Service
class CartService (
@Autowired
private val cartRepository: CartRepository
)
{
fun addProductToCart(cart: Cart, response: ServerHttpResponse) =
cartRepository.insert(cart)
.map {
response.statusCode = HttpStatus.OK
"Product added to cart"
}
.onErrorResume {
response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
Mono.just("Product already exist in cart")
}
fun getProductQuantityInCart(userId: String, productId: String) =
cartRepository.findByUserIdAndProductId(userId, productId)
.map{
it.quantity
}
.defaultIfEmpty(0)
fun getUserCart(userId: String) =
cartRepository.findByUserId(userId)
.map {
it.productId
}
fun updateProductQuantity(cart: Cart, response: ServerHttpResponse) =
cartRepository.existsByUserIdAndProductId(cart.userId, cart.productId)
.flatMap {
if (it) {
cartRepository.deleteByUserIdAndProductId(cart.userId, cart.productId)
.flatMap {
cartRepository.insert(cart)
}
.then(Mono.just("Product quantity updated"))
}
else {
response.statusCode = HttpStatus.NOT_FOUND
Mono.just("Product not found")
}
}
fun deleteProductFromCart(userId: String, productId: String, response: ServerHttpResponse) =
cartRepository.existsByUserIdAndProductId(userId, productId)
.flatMap {
if (it) {
cartRepository.deleteByUserIdAndProductId(userId, productId)
.then(Mono.just("Product removed from cart"))
}
else {
response.statusCode = HttpStatus.NOT_FOUND
Mono.just("Product not found in cart")
}
}
fun emptyUserCart(userId: String, response: ServerHttpResponse) =
cartRepository.existsByUserId(userId)
.flatMap {
if (! it) {
response.statusCode = HttpStatus.NOT_FOUND
Mono.just("Cart not found")
}
else {
cartRepository.deleteByUserId(userId)
.then(Mono.just("Successful"))
}
}
}
4 changes: 3 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@

spring.data.mongodb.uri=${MONGODB_URI}
spring.data.mongodb.database=HRV-Mart-Backend-Cart
server.port=${APPLICATION_PORT}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.example.backendcart.controller

import com.example.backendcart.model.Cart
import com.example.backendcart.repository.CartRepository
import com.example.backendcart.service.CartService
import org.junit.jupiter.api.Test
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.springframework.http.server.reactive.ServerHttpResponse
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.test.StepVerifier

class CartControllerTest {
private val response = mock(ServerHttpResponse::class.java)
private val cartRepository = mock(CartRepository::class.java)
private val cartService = CartService(cartRepository)
private val cartController = CartController(cartService)
private val cart = Cart(
userId = "User ID",
productId = "Product ID",
quantity = 2
)
@Test
fun `should add product in cart if it is not present in cart`() {
doReturn(Mono.just(cart))
.`when`(cartRepository)
.insert(cart)
StepVerifier.create(cartController.addProductInCart(cart, response))
.expectNext("Product added to cart")
.verifyComplete()
}
@Test
fun `should not add product in cart if it is present in cart`() {
doReturn(Mono.error<Exception>(Exception("Product already exist in cart")))
.`when`(cartRepository)
.insert(cart)
StepVerifier.create(cartController.addProductInCart(cart, response))
.expectNext("Product already exist in cart")
.verifyComplete()
}
@Test
fun `should get product quantity if product available in cart`() {
doReturn(Mono.just(cart))
.`when`(cartRepository)
.findByUserIdAndProductId(cart.userId, cart.productId)
StepVerifier.create(cartController.getProductQuantityInCart(cart.productId, cart.userId))
.expectNext(cart.quantity)
.verifyComplete()
}
@Test
fun `should get product quantity as 0 if product is not available in cart`() {
doReturn(Mono.empty<Cart>())
.`when`(cartRepository)
.findByUserIdAndProductId(cart.userId, cart.productId)
StepVerifier.create(cartController.getProductQuantityInCart(cart.productId, cart.userId))
.expectNext(0)
.verifyComplete()
}
@Test
fun `should get productId present in user cart`() {
doReturn(Flux.just(cart))
.`when`(cartRepository)
.findByUserId(cart.userId)
StepVerifier.create(cartController.getUserCart(cart.userId))
.expectNext(cart.productId)
.verifyComplete()
}
@Test
fun `should update product quantity when it is available in cart`() {
val newCart = Cart(
userId = cart.userId,
productId = cart.productId,
quantity = 20
)
doReturn(Mono.just(true))
.`when`(cartRepository)
.existsByUserIdAndProductId(cart.userId, cart.productId)
doReturn(Mono.empty<Cart>())
.`when`(cartRepository)
.deleteByUserIdAndProductId(cart.userId, cart.productId)
doReturn(Mono.just(newCart))
.`when`(cartRepository)
.insert(newCart)
StepVerifier.create(cartController.updateProductQuantity(newCart, response))
.expectNext("Product quantity updated")
.verifyComplete()
}
@Test
fun `should not update product quantity when it is not available in cart`() {
val newCart = Cart(
userId = cart.userId,
productId = cart.productId,
quantity = 20
)
doReturn(Mono.just(false))
.`when`(cartRepository)
.existsByUserIdAndProductId(cart.userId, cart.productId)
StepVerifier.create(cartController.updateProductQuantity(newCart, response))
.expectNext("Product not found")
.verifyComplete()
}
@Test
fun `should delete product from cart when it is available in cart`() {
doReturn(Mono.just(true))
.`when`(cartRepository)
.existsByUserIdAndProductId(cart.userId, cart.productId)
doReturn(Mono.empty<Void>())
.`when`(cartRepository)
.deleteByUserIdAndProductId(cart.userId, cart.productId)
StepVerifier.create(cartController.deleteProductFromCart(cart.productId, cart.userId, response))
.expectNext("Product removed from cart")
.verifyComplete()
}
@Test
fun `should not delete product from cart when it is not available in cart`() {
doReturn(Mono.just(false))
.`when`(cartRepository)
.existsByUserIdAndProductId(cart.userId, cart.productId)
StepVerifier.create(cartController.deleteProductFromCart(cart.productId, cart.userId, response))
.expectNext("Product not found in cart")
.verifyComplete()
}
@Test
fun `should empty cart when products are available in cart`() {
doReturn(Mono.just(true))
.`when`(cartRepository)
.existsByUserId(cart.userId)
doReturn(Flux.empty<Void>())
.`when`(cartRepository)
.deleteByUserId(cart.userId)
StepVerifier.create(cartController.emptyCart(cart.userId, response))
.expectNext("Successful")
.verifyComplete()
}
@Test
fun `should not empty cart when products are not available in cart`() {
doReturn(Mono.just(false))
.`when`(cartRepository)
.existsByUserId(cart.userId)
StepVerifier.create(cartController.emptyCart(cart.userId, response))
.expectNext("Cart not found")
.verifyComplete()
}
}

0 comments on commit a695638

Please sign in to comment.