Skip to content

Feat/23 implement Bookmark #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ repositories {
}

dependencies {
// implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("com.ninja-squad:springmockk:4.0.2")
developmentOnly("org.springframework.boot:spring-boot-devtools")
// runtimeOnly("com.mysql:mysql-connector-j")
runtimeOnly("com.mysql:mysql-connector-j")
testImplementation("org.springframework.boot:spring-boot-starter-test")
// implementation("org.modelmapper:modelmapper:2.4.2")
testImplementation("com.h2database:h2")
implementation("org.modelmapper:modelmapper:2.4.2")
}

tasks.withType<KotlinCompile> {
Expand Down
16 changes: 16 additions & 0 deletions src/main/kotlin/com/group4/ticketingservice/config/Config.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.group4.ticketingservice.config

import org.modelmapper.ModelMapper
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class Config {

@Bean
fun modelMapper(): ModelMapper {
val modelMapper = ModelMapper()
modelMapper.configuration.isFieldMatchingEnabled = true
return modelMapper
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.group4.ticketingservice.controller

import com.group4.ticketingservice.dto.BookmarkFromdto
import com.group4.ticketingservice.service.BookmarkService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController // REST API
@RequestMapping("bookmarks")
class BookmarkController @Autowired constructor(val bookmarkService: BookmarkService) {

// 북마크 등록
@PostMapping
fun addBookmark(boardFormDto: BookmarkFromdto): ResponseEntity<Any> {
val savedBookmarkId = bookmarkService.create(boardFormDto)
val headers = HttpHeaders()
headers.set("Content-Location", "/bookmark/%d".format(savedBookmarkId))
return ResponseEntity.status(HttpStatus.CREATED).headers(headers).body(savedBookmarkId)
}

// 특정 북마크 조회하기
@GetMapping("/{id}")
fun getBookmark(@PathVariable id: Int): ResponseEntity<out Any?> {
try {
val foundBookmark = bookmarkService.get(id)
return ResponseEntity.status(HttpStatus.OK).body(foundBookmark ?: "null")
} catch (e: MethodArgumentNotValidException) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build()
}
}

// 북마크 삭제
@DeleteMapping("/{id}")
fun deleteBookmark(@PathVariable id: Int): ResponseEntity<Any> {
bookmarkService.delete(id)
return ResponseEntity.status(HttpStatus.NO_CONTENT).build()
}

// 전체사용자 북마크 목록
@GetMapping()
fun getBookmarks(): ResponseEntity<Any> {
return ResponseEntity.status(HttpStatus.OK).body(bookmarkService.getList())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.group4.ticketingservice.dto

data class BookmarkFromdto(
var user_id: Int,
var show_id: Int
)
15 changes: 15 additions & 0 deletions src/main/kotlin/com/group4/ticketingservice/entity/Bookmark.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.group4.ticketingservice.entity

import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id

@Entity
class Bookmark(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int? = null,
var user_id: Int, // 사용자 식별자
var show_id: Int // 공연 식별자
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.group4.ticketingservice.repository

import com.group4.ticketingservice.entity.Bookmark
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface BookmarkRepository : JpaRepository<Bookmark, Long>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.group4.ticketingservice.service

import com.group4.ticketingservice.dto.BookmarkFromdto
import com.group4.ticketingservice.entity.Bookmark
import com.group4.ticketingservice.repository.BookmarkRepository
import org.modelmapper.ModelMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class BookmarkService @Autowired constructor(
val bookmarkRepository: BookmarkRepository,
val modelMapper: ModelMapper
) {

fun create(bookmarkFormDto: BookmarkFromdto): Int? {
return bookmarkRepository.save(modelMapper.map(bookmarkFormDto, Bookmark::class.java)).id
}

fun get(id: Int): Bookmark? {
return bookmarkRepository.findByIdOrNull(id.toLong())
}

fun delete(id: Int) {
bookmarkRepository.deleteById(id.toLong())
}
fun getList(): List<Bookmark> {
return bookmarkRepository.findAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.springframework.boot.test.context.SpringBootTest
class TicketingserviceApplicationTests {

@Test
fun contextLoads() {
fun contextLoads(): Boolean {
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package com.group4.ticketingservice.bookmark

import com.group4.ticketingservice.controller.BookmarkController
import com.group4.ticketingservice.dto.BookmarkFromdto
import com.group4.ticketingservice.entity.Bookmark
import com.group4.ticketingservice.service.BookmarkService
import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import io.mockk.verify
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@WebMvcTest(BookmarkController::class)
class BookmarkControllerTest(@Autowired val mockMvc: MockMvc) {
@MockkBean
private lateinit var service: BookmarkService
private val sampleBookmark = Bookmark(
user_id = 1,
show_id = 1
)
private val sampleBookmarkDto = BookmarkFromdto(
user_id = 1,
show_id = 1
)

@Test
fun `POST_api_bookmark should invoke service_create`() {
// given
every { service.create(sampleBookmarkDto) } returns 1

// when
mockMvc.perform(
post("/bookmarks")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("user_id", sampleBookmark.user_id.toString())
.param("show_id", sampleBookmark.show_id.toString())
)

// then
verify(exactly = 1) { service.create(sampleBookmarkDto) }
}

@Test
fun `POST_api_bookmark should return saved bookmark id with HTTP 201 Created`() {
// given
every { service.create(sampleBookmarkDto) } returns 1

// when
val resultActions: ResultActions = mockMvc.perform(
post("/bookmarks")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("user_id", sampleBookmark.user_id.toString())
.param("show_id", sampleBookmark.show_id.toString())
)

// then
resultActions.andExpect(status().isCreated)
.andExpect(content().json("1"))
}

@Test
fun `POST_api_bookmark should return HTTP ERROR 400 for invalid parameter`() {
// given
every { service.create(sampleBookmarkDto) } returns 1

// when
val resultActions: ResultActions = mockMvc.perform(
post("/bookmarks")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("user_id", sampleBookmark.user_id.toString())
.param("show_id", sampleBookmark.show_id.toString())
)

// then
resultActions.andExpect(status().isCreated)
.andExpect(content().json("1"))
}

@Test
fun `GET_api_bookmarks should invoke service_getList`() {
// given
every { service.getList() } returns mutableListOf(sampleBookmark)

// when
mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks"))

// then
verify(exactly = 1) { service.getList() }
}

@Test
fun `GET_api_bookmarks should return list of bookmarks with HTTP 200 OK`() {
// given
every { service.getList() } returns mutableListOf(sampleBookmark)

// when
val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks"))

// then
resultActions.andExpect(status().isOk)
.andExpect(jsonPath("$[0].user_id").value(sampleBookmark.user_id))
}

@Test
fun `GET_api_bookmark should invoke service_get`() {
// given
every { service.get(1) } returns sampleBookmark

// when
mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks/1"))

// then
verify(exactly = 1) { service.get(1) }
}

@Test
fun `GET_api_bookmark should return found bookmark with HTTP 200 OK`() {
// given
every { service.get(1) } returns sampleBookmark

// when
val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks/1"))

// then
resultActions.andExpect(status().isOk)
.andExpect(jsonPath("$.id").value(sampleBookmark.id))
.andExpect(jsonPath("$.user_id").value(sampleBookmark.user_id))
.andExpect(jsonPath("$.show_id").value(sampleBookmark.show_id))
}

@Test
fun `GET_api_bookmark should return null with HTTP 200 OK if element is not found`() {
// given
every { service.get(1) } returns null

// when
val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks/1"))

// then
resultActions.andExpect(status().isOk)
.andExpect(content().string("null"))
}

@Test
fun `DELETE_api_bookmark_{bookmarkId} should invoke service_delete`() {
// given
every { service.delete(1) } returns Unit

// when
mockMvc.perform(
MockMvcRequestBuilders
.delete("/bookmarks/1")
)

// then
verify(exactly = 1) { service.delete(1) }
}

@Test
fun `DELETE_api_bookmark_{bookmarkId} should return HTTP 204 No Content`() {
// given
every { service.delete(1) } returns Unit

// when
val resultActions: ResultActions = mockMvc.perform(
MockMvcRequestBuilders
.delete("/bookmarks/1")
)

// then
resultActions.andExpect(status().isNoContent)
}
}
Loading