-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add article listing functionality with integration tests
- Loading branch information
Showing
15 changed files
with
302 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 7 additions & 1 deletion
8
src/main/kotlin/io/github/gunkim/realworld/config/ObjectMapperConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,18 @@ | ||
package io.github.gunkim.realworld.config | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.fasterxml.jackson.databind.SerializationFeature | ||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule | ||
import com.fasterxml.jackson.module.kotlin.registerKotlinModule | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
|
||
@Configuration | ||
class ObjectMapperConfig { | ||
@Bean | ||
fun objectMapper() = ObjectMapper().registerKotlinModule() | ||
fun objectMapper() = ObjectMapper().apply { | ||
registerKotlinModule() | ||
registerModule(JavaTimeModule()) | ||
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/main/kotlin/io/github/gunkim/realworld/domain/article/service/FavoriteArticleService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package io.github.gunkim.realworld.domain.article.service | ||
|
||
import io.github.gunkim.realworld.domain.article.ArticleCountProjection | ||
import io.github.gunkim.realworld.domain.article.ArticleReadRepository | ||
import java.util.UUID | ||
import org.springframework.stereotype.Service | ||
|
||
@Service | ||
class FavoriteArticleService( | ||
private val articleReadRepository: ArticleReadRepository, | ||
) { | ||
fun getFavoritesCount(articleUuids: List<UUID>): List<ArticleCountProjection> = | ||
articleReadRepository.getCountAllByArticleUuids(articleUuids) | ||
|
||
fun getFavoritesArticles(userUuid: UUID): List<UUID> = articleReadRepository.getFavoritesArticles(userUuid) | ||
} |
24 changes: 24 additions & 0 deletions
24
src/main/kotlin/io/github/gunkim/realworld/domain/article/service/GetArticleService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package io.github.gunkim.realworld.domain.article.service | ||
|
||
import io.github.gunkim.realworld.domain.article.Article | ||
import io.github.gunkim.realworld.domain.article.ArticleReadRepository | ||
import org.springframework.stereotype.Service | ||
|
||
@Service | ||
class GetArticleService( | ||
private val articleReadRepository: ArticleReadRepository, | ||
) { | ||
fun getArticles( | ||
tag: String?, | ||
author: String?, | ||
limit: Int = 20, | ||
offset: Int = 0, | ||
): List<Article> { | ||
return articleReadRepository.find( | ||
tag = tag, | ||
author = author, | ||
limit = limit, | ||
offset = offset | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/main/kotlin/io/github/gunkim/realworld/web/api/article/ArticlesController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package io.github.gunkim.realworld.web.api.article | ||
|
||
import io.github.gunkim.realworld.domain.article.Article | ||
import io.github.gunkim.realworld.domain.article.service.FavoriteArticleService | ||
import io.github.gunkim.realworld.domain.article.service.GetArticleService | ||
import io.github.gunkim.realworld.domain.user.service.FollowUserService | ||
import io.github.gunkim.realworld.share.AuthenticatedUser | ||
import io.github.gunkim.realworld.web.api.article.model.request.GetArticlesRequest | ||
import io.github.gunkim.realworld.web.api.article.model.response.ArticleResponse | ||
import io.github.gunkim.realworld.web.api.article.model.response.ArticlesResponse | ||
import java.util.UUID | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
@RequestMapping("/api/articles") | ||
interface ArticleResource { | ||
@GetMapping | ||
fun getArticles( | ||
request: GetArticlesRequest, | ||
@AuthenticationPrincipal authenticatedUser: AuthenticatedUser?, | ||
): ArticlesResponse | ||
} | ||
|
||
@RestController | ||
class ArticlesController( | ||
private val getArticleService: GetArticleService, | ||
private val favoriteArticleService: FavoriteArticleService, | ||
private val followUserService: FollowUserService, | ||
) : ArticleResource { | ||
override fun getArticles( | ||
request: GetArticlesRequest, | ||
authenticatedUser: AuthenticatedUser?, | ||
): ArticlesResponse { | ||
val articles = getArticleService.getArticles( | ||
tag = request.tag, | ||
author = request.author, | ||
offset = request.offset, | ||
limit = request.limit, | ||
) | ||
return articlesResponse(articles, authenticatedUser) | ||
} | ||
|
||
private fun articlesResponse( | ||
articles: List<Article>, | ||
authenticatedUser: AuthenticatedUser?, | ||
): ArticlesResponse { | ||
val articleUuids = articles.map(Article::uuid) | ||
val favoritesCountMap = if (articleUuids.isEmpty()) { | ||
emptyList() | ||
} else { | ||
favoriteArticleService.getFavoritesCount(articleUuids) | ||
} | ||
val (favoritedArticleUuids, followingUserUuids) = getUserContext(authenticatedUser) | ||
|
||
return ArticlesResponse(articles.map { article -> | ||
ArticleResponse.from( | ||
article, | ||
favoritesCountMap.firstOrNull { it.getUuid() == article.uuid }?.getCount() ?: 0, | ||
favoritedArticleUuids.contains(article.uuid), | ||
followingUserUuids.contains(article.author.uuid) | ||
) | ||
}) | ||
} | ||
|
||
private fun getUserContext(authenticatedUser: AuthenticatedUser?): Pair<List<UUID>, List<UUID>> { | ||
return if (authenticatedUser != null) { | ||
val userUuid = authenticatedUser.uuid | ||
val favoritedArticleUuids = favoriteArticleService.getFavoritesArticles(userUuid) | ||
val followingUserUuids = followUserService.getFollowingUserUuids(userUuid) | ||
favoritedArticleUuids to followingUserUuids | ||
} else { | ||
emptyList<UUID>() to emptyList() | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...ain/kotlin/io/github/gunkim/realworld/web/api/article/model/request/GetArticlesRequest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package io.github.gunkim.realworld.web.api.article.model.request | ||
|
||
data class GetArticlesRequest( | ||
val tag: String?, | ||
val author: String?, | ||
val favorited: String?, | ||
val limit: Int = 20, | ||
val offset: Int = 0, | ||
) |
38 changes: 38 additions & 0 deletions
38
src/main/kotlin/io/github/gunkim/realworld/web/api/article/model/response/ArticleResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package io.github.gunkim.realworld.web.api.article.model.response | ||
|
||
import com.fasterxml.jackson.annotation.JsonTypeInfo | ||
import io.github.gunkim.realworld.domain.article.Article | ||
import io.github.gunkim.realworld.web.api.user.model.response.ProfileResponse | ||
import java.time.Instant | ||
|
||
data class ArticleResponse( | ||
val slug: String, | ||
val title: String, | ||
val description: String, | ||
val body: String, | ||
val tagList: List<String>, | ||
val createdAt: Instant, | ||
val updatedAt: Instant, | ||
val favorited: Boolean, | ||
val favoritesCount: Int, | ||
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) | ||
val author: ProfileResponse, | ||
) { | ||
companion object { | ||
fun from(article: Article, favoritesCount: Int, favorited: Boolean, authorFollowing: Boolean) = ArticleResponse( | ||
slug = article.slug, | ||
title = article.title, | ||
description = article.description, | ||
body = article.body, | ||
tagList = article.tags.map { it.name }, | ||
createdAt = article.createdAt, | ||
updatedAt = article.updatedAt, | ||
favorited = favorited, | ||
favoritesCount = favoritesCount, | ||
author = ProfileResponse.of( | ||
article.author, | ||
authorFollowing | ||
) | ||
) | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
...main/kotlin/io/github/gunkim/realworld/web/api/article/model/response/ArticlesResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package io.github.gunkim.realworld.web.api.article.model.response | ||
|
||
data class ArticlesResponse( | ||
val articles: List<ArticleResponse>, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.