Skip to content
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

[Refactor] JSON으로 전환 #96

Open
wants to merge 3 commits into
base: albertimkr
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 8 additions & 4 deletions http/url-shortener.http
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
### Origin URL 저장
POST {{baseUrl}}/shorten-url/create
Content-Type: text/plain
Content-Type: application/json

https://www.naver.com
{
"originUrl": "https://www.naver.com"
}

### 짧은 키워드로 Origin URL 조회
POST {{baseUrl}}/shorten-url/search
Content-Type: text/plain
Content-Type: application/json

1
{
"shortenUrl": "1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package community.whatever.onembackendkotlin

data class ShortenUrlCreateRequest(val originUrl: String)

data class ShortenUrlSearchRequest(val shortenUrl: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package community.whatever.onembackendkotlin

data class ShortenedUrlResponse(val shortenedUrl: String)

data class OriginUrlResponse(val originUrl: String)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package community.whatever.onembackendkotlin

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
Expand All @@ -8,12 +9,12 @@ import org.springframework.web.bind.annotation.RestController
class UrlShortenController(private val urlShortenService: UrlShortenService) {

@PostMapping("/shorten-url/search")
fun shortenUrlSearch(@RequestBody key: String): String {
return urlShortenService.getOriginUrl(key)
fun shortenUrlSearch(@RequestBody key: ShortenUrlSearchRequest): ResponseEntity<OriginUrlResponse> {
return ResponseEntity.ok(urlShortenService.getOriginUrl(key))
}

@PostMapping("/shorten-url/create")
fun shortenUrlCreate(@RequestBody originUrl: String): String {
return urlShortenService.saveShortenUrl(originUrl)
fun shortenUrlCreate(@RequestBody originUrl: ShortenUrlCreateRequest): ResponseEntity<ShortenedUrlResponse> {
return ResponseEntity.ok(urlShortenService.saveShortenUrl(originUrl))
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
package community.whatever.onembackendkotlin

interface UrlShortenService {
/**
* 아이디를 통해 원본 URL을 찾는다.
*
* @param id ShortenedUrl의 id이다.
* @return ShortenedUrl의 id
* @throws UrlNotFoundException ShortenedUrl의 id에 해당하는 ShortenedUrl이 존재하지 않으면 발생한다.
*/
fun getOriginUrl(id: String): String

/**
* URL이 존재하면 저장된 URL의 id를 반환하고 존재하지 않으면 새롭게 URL을 저장하고 id를 반환한다.
*
* @param originUrl 단축할 URL이다.
* @return 저장된 URL의 id이다.
*/
fun saveShortenUrl(originUrl: String): String
fun getOriginUrl(request: ShortenUrlSearchRequest): OriginUrlResponse
fun saveShortenUrl(request: ShortenUrlCreateRequest): ShortenedUrlResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import org.springframework.stereotype.Service

@Service
class UrlShortenServiceImpl(private val shortenedUrlRepository: ShortenedUrlRepository) : UrlShortenService {
override fun getOriginUrl(id: String): String {
return shortenedUrlRepository.findById(id)?.originUrl ?: throw UrlNotFoundException()
override fun getOriginUrl(request: ShortenUrlSearchRequest): OriginUrlResponse {
val id = request.shortenUrl
return OriginUrlResponse(shortenedUrlRepository.findById(id)?.originUrl ?: throw UrlNotFoundException())
}

override fun saveShortenUrl(originUrl: String): String {
override fun saveShortenUrl(request: ShortenUrlCreateRequest): ShortenedUrlResponse {
val originUrl = request.originUrl
if (shortenedUrlRepository.existsByOriginUrl(originUrl)) {
return shortenedUrlRepository.findByOriginUrl(originUrl)!!.id!!
return ShortenedUrlResponse(shortenedUrlRepository.findByOriginUrl(originUrl)!!.id!!)
}
return shortenedUrlRepository.save(ShortenedUrl(originUrl)).id!!
return ShortenedUrlResponse(shortenedUrlRepository.save(ShortenedUrl(originUrl)).id!!)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit 비즈니스 레이어에 프레젠테이션 레이어의 객체가 있는 것은 선호하지 않습니다.



}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.awaitility.kotlin.untilAsserted
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.springframework.beans.factory.annotation.Autowired
Expand Down Expand Up @@ -42,32 +43,27 @@ class UrlShortenControllerIntegrationTest {
)
fun `등록된 원본 URL을 키로 검색하면 해당 원본 URL을 반환한다`(originUrl: String) = runTest {
// given
val key = createUrl(originUrl)
.expectBody(String::class.java)
val key = createUrl(ShortenUrlCreateRequest(originUrl))
.expectBody(ShortenedUrlResponse::class.java)
.returnResult()
.responseBody!!

// when
val result = searchShortenUrl(key)
val result = searchShortenUrl(ShortenUrlSearchRequest(key.shortenedUrl))

// then
result.expectStatus().isOk()
.expectBody(String::class.java)
.isEqualTo(originUrl)
.expectBody(OriginUrlResponse::class.java)
.isEqualTo(OriginUrlResponse(originUrl))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi. jsonPath

}

@ParameterizedTest
@CsvSource(
"https://www.google.com",
"https://www.naver.com",
"https://www.daum.net"
)
fun `등록되지 않은 원본 URL을 키로 검색하면 404를 반환한다`(originUrl: String) = runTest {
@Test
fun `등록되지 않은 원본 URL을 키로 검색하면 404를 반환한다`() = runTest {
// given
val key = "non-exist-key"

// when
val result = searchShortenUrl(key)
val result = searchShortenUrl(ShortenUrlSearchRequest(key))

// then
result.expectStatus()
Expand All @@ -87,14 +83,14 @@ class UrlShortenControllerIntegrationTest {
)
fun `원본 URL을 등록하면 키를 반환한다`(originUrl: String) = runTest {
// when
val result = createUrl(originUrl)
val result = createUrl(ShortenUrlCreateRequest(originUrl))

// then
val key = result.expectStatus().isOk()
.expectBody(String::class.java)
val shortenedUrlResponse = result.expectStatus().isOk()
.expectBody(ShortenedUrlResponse::class.java)
.returnResult()
.responseBody!!
assertThat(key).isNotBlank()
assertThat(shortenedUrlResponse.shortenedUrl).isNotBlank()
}

@ParameterizedTest
Expand All @@ -104,7 +100,8 @@ class UrlShortenControllerIntegrationTest {
val originUrls = (1..count).map { "https://www.google.com/$it" }

// when
val keys = originUrls.map { async { createUrlAndReturnKey(it) } }
val keys = originUrls.map { async { createUrlAndReturnKey(ShortenUrlCreateRequest(it)) } }
.map { it.await() }

// then
await untilAsserted { assertThat(keys).hasSize(count).doesNotHaveDuplicates() }
Expand All @@ -118,10 +115,10 @@ class UrlShortenControllerIntegrationTest {
)
fun `등록된 원본 URL을 다시 등록하면 기존 키를 반환한다`(originUrl: String) = runTest {
// given
val key = createUrlAndReturnKey(originUrl)
val key = createUrlAndReturnKey(ShortenUrlCreateRequest(originUrl))

// when
val result = createUrl(originUrl)
val result = createUrl(ShortenUrlCreateRequest(originUrl))

// then
result.expectStatus().isOk()
Expand All @@ -130,17 +127,17 @@ class UrlShortenControllerIntegrationTest {
}
}

private fun searchShortenUrl(key: String) = webTestClient.post()
private fun searchShortenUrl(request: ShortenUrlSearchRequest) = webTestClient.post()
.uri("/shorten-url/search")
.bodyValue(key)
.bodyValue(request)
.exchange()

private fun createUrl(originUrl: String) = webTestClient.post()
private fun createUrl(request: ShortenUrlCreateRequest) = webTestClient.post()
.uri("/shorten-url/create")
.bodyValue(originUrl)
.bodyValue(request)
.exchange()

private fun createUrlAndReturnKey(originUrl: String) = createUrl(originUrl)
private fun createUrlAndReturnKey(request: ShortenUrlCreateRequest) = createUrl(request)
.expectBody(String::class.java)
.returnResult()
.responseBody!!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package community.whatever.onembackendkotlin

import com.fasterxml.jackson.databind.ObjectMapper
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
Expand All @@ -19,18 +20,20 @@ class UrlShortenControllerTest(
@MockitoBean
private lateinit var urlShortenService: UrlShortenService

private val objectMapper = ObjectMapper()

@Test
fun `원본 URL 등록 시 키를 반환한다`() {
// given
val originUrl = "https://www.google.com"
val key = "123"
val originUrl = ShortenUrlCreateRequest("https://www.google.com")
val key = ShortenedUrlResponse("123")
given(urlShortenService.saveShortenUrl(originUrl)).willReturn(key)

// when
val result = mockMvc.perform(
post("/shorten-url/create")
.contentType(MediaType.APPLICATION_JSON)
.content(originUrl)
.content(objectMapper.writeValueAsString(originUrl))
)
.andExpect(status().isOk)
.andReturn()
Expand All @@ -44,59 +47,59 @@ class UrlShortenControllerTest(
@Test
fun `키로 검색하면 원본 URL을 반환한다`() {
// given
val originUrl = "https://www.google.com"
val key = "123"
val originUrl = OriginUrlResponse("https://www.google.com")
val key = ShortenUrlSearchRequest("123")
given(urlShortenService.getOriginUrl(key)).willReturn(originUrl)

// when
val result = mockMvc.perform(
post("/shorten-url/search")
.contentType(MediaType.APPLICATION_JSON)
.content(key)
.content(objectMapper.writeValueAsString(key))
)
.andExpect(status().isOk)
.andReturn()
.response
.contentAsString

// then
assertThat(originUrl).isEqualTo(result)
assertThat(result).isEqualTo(objectMapper.writeValueAsString(originUrl))
}

@Test
fun `유요하지 않은 키로 검색하면 404 에러를 반환한다`() {
// given
val invalidKey = "invalid-key"
val invalidKey = ShortenUrlSearchRequest("invalid")
given(urlShortenService.getOriginUrl(invalidKey)).willThrow(UrlNotFoundException())

// when
mockMvc.perform(
post("/shorten-url/search")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidKey)
.content(objectMapper.writeValueAsString(invalidKey))
)
.andExpect(status().isNotFound)
}

@Test
fun `중복된 원본 URL을 등록하면 기존 키를 반환한다`() {
// given
val originUrl = "https://www.google.com"
val key = "123"
val originUrl = ShortenUrlCreateRequest("https://www.google.com")
val key = ShortenedUrlResponse("123")
given(urlShortenService.saveShortenUrl(originUrl)).willReturn(key)

// when
val result = mockMvc.perform(
post("/shorten-url/create")
.contentType(MediaType.APPLICATION_JSON)
.content(originUrl)
.content(objectMapper.writeValueAsString(originUrl))
)
.andExpect(status().isOk)
.andReturn()
.response
.contentAsString

// then
assertThat(key).isEqualTo(result)
assertThat(result).isEqualTo(objectMapper.writeValueAsString(key))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ class UrlShortenServiceImplTest {
every { shortenedUrlRepository.save(shortenedUrl) } returns savedShortenedUrl

// when
val result = urlShortenService.saveShortenUrl(originUrl)
val result = urlShortenService.saveShortenUrl(ShortenUrlCreateRequest(originUrl))

// then
assertThat(result).isEqualTo(id)
assertThat(result).isEqualTo(ShortenedUrlResponse(id))
verify { shortenedUrlRepository.save(shortenedUrl) }
}

Expand All @@ -54,10 +54,10 @@ class UrlShortenServiceImplTest {
every { shortenedUrlRepository.findByOriginUrl(originUrl) } returns savedShortenedUrl

// when
val result = urlShortenService.saveShortenUrl(originUrl)
val result = urlShortenService.saveShortenUrl(ShortenUrlCreateRequest(originUrl))

// then
assertThat(result).isEqualTo(id)
assertThat(result).isEqualTo(ShortenedUrlResponse(id))
verify { shortenedUrlRepository.findByOriginUrl(originUrl) }
}
}
Expand All @@ -83,10 +83,10 @@ class UrlShortenServiceImplTest {
every { shortenedUrlRepository.findById(id) } returns shortenedUrl

// when
val result = urlShortenService.getOriginUrl(id)
val result = urlShortenService.getOriginUrl(ShortenUrlSearchRequest(id))

// then
assertThat(result).isEqualTo(originUrl)
assertThat(result).isEqualTo(OriginUrlResponse(originUrl))
verify { shortenedUrlRepository.findById(id) }
}

Expand All @@ -96,7 +96,8 @@ class UrlShortenServiceImplTest {
every { shortenedUrlRepository.findById(id) } returns null

// when, then
assertThatThrownBy { urlShortenService.getOriginUrl(id) }.isInstanceOf(UrlNotFoundException::class.java)
assertThatThrownBy { urlShortenService.getOriginUrl(ShortenUrlSearchRequest(id)) }
.isInstanceOf(UrlNotFoundException::class.java)
verify { shortenedUrlRepository.findById(id) }
}
}
Expand Down