Skip to content

Commit

Permalink
add purchase feature and completed purchases listing
Browse files Browse the repository at this point in the history
  • Loading branch information
angelodpadron committed Nov 6, 2024
1 parent 9b4a93a commit 12d22c3
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.meliapp.backend.controller

import org.meliapp.backend.dto.ApiResponse
import org.meliapp.backend.dto.purchase.PurchaseRequest
import org.meliapp.backend.dto.purchase.PurchaseResponse
import org.meliapp.backend.service.PurchaseService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/purchases")
class PurchaseController(
private val purchaseService: PurchaseService
) {

@PostMapping
fun buy(@RequestBody purchaseRequest: PurchaseRequest): ResponseEntity<ApiResponse<PurchaseResponse>> {
return ResponseEntity.ok(ApiResponse(purchaseService.buy(purchaseRequest)))
}

@GetMapping
fun purchases(): ResponseEntity<ApiResponse<List<PurchaseResponse>>> {
return ResponseEntity.ok(ApiResponse(purchaseService.purchases()))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.meliapp.backend.dto.product

import com.fasterxml.jackson.annotation.JsonProperty
import java.math.BigDecimal

data class ProductPurchaseResponse(
@JsonProperty("meli_id")
val meliId: String,
val title: String,
val thumbnail: String,
val price: BigDecimal,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.meliapp.backend.dto.purchase

import com.fasterxml.jackson.annotation.JsonProperty

data class PurchaseRequest(
@JsonProperty(value = "meli_id")
val meliId: String,
val quantity: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.meliapp.backend.dto.purchase

import com.fasterxml.jackson.annotation.JsonProperty
import org.meliapp.backend.dto.product.ProductPurchaseResponse
import java.math.BigDecimal
import java.time.LocalDateTime

data class PurchaseResponse(
val id: Long,
val quantity: Int,
val product: ProductPurchaseResponse,
@JsonProperty("created_at")
val createdAt: LocalDateTime,
val total: BigDecimal,
)
22 changes: 22 additions & 0 deletions src/main/kotlin/org/meliapp/backend/model/Purchase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.meliapp.backend.model

import jakarta.persistence.*
import org.hibernate.annotations.CreationTimestamp
import java.math.BigDecimal
import java.time.LocalDateTime

@Entity
class Purchase {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0
@ManyToOne
lateinit var user: User
@ManyToOne
lateinit var product: Product
var quantity: Int = 0
@CreationTimestamp
var purchaseDate: LocalDateTime = LocalDateTime.now()
var totalPrice: BigDecimal = BigDecimal.ZERO
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.meliapp.backend.repository

import org.meliapp.backend.model.Purchase
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param

interface PurchaseRepository : JpaRepository<Purchase, Long> {
@Query("SELECT p FROM Purchase p WHERE p.user.id = :id")
fun findByUserId(@Param("id") id: Long): List<Purchase>
}
59 changes: 59 additions & 0 deletions src/main/kotlin/org/meliapp/backend/service/PurchaseService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.meliapp.backend.service

import org.meliapp.backend.dto.product.ProductPurchaseResponse
import org.meliapp.backend.dto.purchase.PurchaseRequest
import org.meliapp.backend.dto.purchase.PurchaseResponse
import org.meliapp.backend.model.Product
import org.meliapp.backend.model.Purchase
import org.meliapp.backend.repository.PurchaseRepository
import org.springframework.stereotype.Service
import java.math.BigDecimal

@Service
class PurchaseService(
private val purchaseRepository: PurchaseRepository,
private val authService: AuthService,
private val productService: ProductService
) {

fun buy(purchaseRequest: PurchaseRequest): PurchaseResponse {
val user = authService.getUserAuthenticated()
val product = productService.findByMeliId(purchaseRequest.meliId)

val purchase = Purchase()
purchase.user = user
purchase.quantity = purchaseRequest.quantity
purchase.product = product
purchase.totalPrice = product.price * BigDecimal(purchaseRequest.quantity)

purchaseRepository.save(purchase)

return toPurchaseResponse(purchase)

}

fun purchases(): List<PurchaseResponse> {
val user = authService.getUserAuthenticated()
return purchaseRepository.findByUserId(user.id).map { toPurchaseResponse(it) }
}

private fun toPurchaseResponse(purchase: Purchase): PurchaseResponse {
return PurchaseResponse(
purchase.id,
purchase.quantity,
toProductPurchaseResponse(purchase.product),
purchase.purchaseDate,
purchase.totalPrice
)
}

private fun toProductPurchaseResponse(product: Product): ProductPurchaseResponse {
return ProductPurchaseResponse(
product.meliId,
product.title,
product.thumbnail,
product.price)
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.meliapp.backend.service

import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.meliapp.backend.dto.purchase.PurchaseRequest
import org.meliapp.backend.model.Product
import org.meliapp.backend.model.Purchase
import org.meliapp.backend.model.User
import org.meliapp.backend.repository.PurchaseRepository
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.kotlin.*
import org.springframework.test.context.junit.jupiter.SpringExtension
import java.math.BigDecimal
import kotlin.test.assertEquals

@ExtendWith(SpringExtension::class)
class PurchaseServiceUnitTest {

@Mock
lateinit var purchaseRepository: PurchaseRepository

@Mock
lateinit var productService: ProductService

@Mock
lateinit var authService: AuthService

@InjectMocks
lateinit var purchaseService: PurchaseService

@BeforeEach
fun setup() {
val user: User = mock()
whenever(user.id).thenReturn(1)
whenever(authService.getUserAuthenticated()).thenReturn(user)
}

@AfterEach
fun cleanup() {
reset(purchaseRepository, productService, authService)
}

@Test
fun `can buy an existing product`() {
val meliId = "MELI_ID"
val productTitle = "TITLE"
val product: Product = mock()

whenever(product.meliId).thenReturn(meliId)
whenever(product.price).thenReturn(BigDecimal.ZERO)
whenever(product.title).thenReturn(productTitle)
whenever(productService.findByMeliId(meliId)).thenReturn(product)

val result = purchaseService.buy(PurchaseRequest(meliId, 1))

assertEquals(meliId, result.product.meliId)
assertEquals(productTitle, result.product.title)
assertEquals(BigDecimal.ZERO, result.product.price)
assertEquals(1, result.quantity)
assertEquals(BigDecimal.ZERO, result.total)

verify(purchaseRepository, times(1)).save(any<Purchase>())

}

@Test
fun `can get all the purchases from current user`() {
whenever(purchaseRepository.findByUserId(any())).thenReturn(listOf())

val result = purchaseService.purchases()

assertEquals(0, result.size)
}

}

0 comments on commit 12d22c3

Please sign in to comment.