diff --git a/http/url-shortener.http b/http/url-shortener.http index 5a1c8cc..165d89c 100644 --- a/http/url-shortener.http +++ b/http/url-shortener.http @@ -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" +} diff --git a/src/main/kotlin/community/whatever/onembackendkotlin/Requests.kt b/src/main/kotlin/community/whatever/onembackendkotlin/Requests.kt new file mode 100644 index 0000000..698f29a --- /dev/null +++ b/src/main/kotlin/community/whatever/onembackendkotlin/Requests.kt @@ -0,0 +1,5 @@ +package community.whatever.onembackendkotlin + +data class ShortenUrlCreateRequest(val originUrl: String) + +data class ShortenUrlSearchRequest(val shortenUrl: String) diff --git a/src/main/kotlin/community/whatever/onembackendkotlin/Responses.kt b/src/main/kotlin/community/whatever/onembackendkotlin/Responses.kt new file mode 100644 index 0000000..04875d4 --- /dev/null +++ b/src/main/kotlin/community/whatever/onembackendkotlin/Responses.kt @@ -0,0 +1,5 @@ +package community.whatever.onembackendkotlin + +data class ShortenedUrlResponse(val shortenedUrl: String) + +data class OriginUrlResponse(val originUrl: String) diff --git a/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenController.kt b/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenController.kt index c59c957..c30547b 100644 --- a/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenController.kt +++ b/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenController.kt @@ -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 @@ -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 { + 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 { + return ResponseEntity.ok(urlShortenService.saveShortenUrl(originUrl)) } } diff --git a/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenService.kt b/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenService.kt index 8f521bc..2b7ae5d 100644 --- a/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenService.kt +++ b/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenService.kt @@ -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 } diff --git a/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImpl.kt b/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImpl.kt index aad7f5e..ec3499e 100644 --- a/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImpl.kt +++ b/src/main/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImpl.kt @@ -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!!) } - - } diff --git a/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerIntegrationTest.kt b/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerIntegrationTest.kt index 581d703..46adee0 100644 --- a/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerIntegrationTest.kt +++ b/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerIntegrationTest.kt @@ -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 @@ -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)) } - @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() @@ -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 @@ -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() } @@ -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() @@ -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!! diff --git a/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerTest.kt b/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerTest.kt index 1a2e9e8..6cae876 100644 --- a/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerTest.kt +++ b/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenControllerTest.kt @@ -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 @@ -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() @@ -44,15 +47,15 @@ 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() @@ -60,20 +63,20 @@ class UrlShortenControllerTest( .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) } @@ -81,15 +84,15 @@ class UrlShortenControllerTest( @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() @@ -97,6 +100,6 @@ class UrlShortenControllerTest( .contentAsString // then - assertThat(key).isEqualTo(result) + assertThat(result).isEqualTo(objectMapper.writeValueAsString(key)) } } diff --git a/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImplTest.kt b/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImplTest.kt index 7ce7750..00461ed 100644 --- a/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImplTest.kt +++ b/src/test/kotlin/community/whatever/onembackendkotlin/UrlShortenServiceImplTest.kt @@ -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) } } @@ -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) } } } @@ -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) } } @@ -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) } } }