Add article query
게시글 단건 조회 추가
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
package com.yam.app.article.application;
|
package com.yam.app.article.application;
|
||||||
|
|
||||||
|
import com.yam.app.article.domain.ArticleNotFoundException;
|
||||||
import com.yam.app.article.domain.ArticleReader;
|
import com.yam.app.article.domain.ArticleReader;
|
||||||
import com.yam.app.article.domain.WriteArticleProcessor;
|
import com.yam.app.article.domain.WriteArticleProcessor;
|
||||||
import com.yam.app.article.presentation.ArticlePreviewResponse;
|
import com.yam.app.article.presentation.ArticlePreviewResponse;
|
||||||
|
import com.yam.app.article.presentation.ArticleResponse;
|
||||||
|
import com.yam.app.article.presentation.TagResponse;
|
||||||
import com.yam.app.article.presentation.WriteArticleCommand;
|
import com.yam.app.article.presentation.WriteArticleCommand;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -38,4 +41,26 @@ public class ArticleFacade {
|
|||||||
dto.getStatus())
|
dto.getStatus())
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ArticleResponse findById(Long articleId) {
|
||||||
|
var article = articleReader.findById(articleId).orElseThrow(
|
||||||
|
() -> new ArticleNotFoundException(articleId));
|
||||||
|
|
||||||
|
return ArticleResponse.builder()
|
||||||
|
.id(article.getId())
|
||||||
|
.authorId(article.getAuthorId())
|
||||||
|
.title(article.getTitle())
|
||||||
|
.content(article.getContent())
|
||||||
|
.image(article.getImage())
|
||||||
|
.createdAt(article.getCreatedAt())
|
||||||
|
.modifiedAt(article.getModifiedAt())
|
||||||
|
.tags(
|
||||||
|
article.getTags().stream()
|
||||||
|
.map(a -> TagResponse.of(a.getTag().getId(), a.getTag().getName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.yam.app.common.ApiResult;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@@ -29,4 +30,9 @@ public final class ArticleQueryApi {
|
|||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
ApiResult.success(articleFacade.findAll(offset, limit)));
|
ApiResult.success(articleFacade.findAll(offset, limit)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/api/articles/{articleId}")
|
||||||
|
public ResponseEntity<ArticleResponse> showArticle(@PathVariable Long articleId) {
|
||||||
|
return ResponseEntity.ok(articleFacade.findById(articleId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.yam.app.article.presentation;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public final class ArticleResponse {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final Long authorId;
|
||||||
|
private final String title;
|
||||||
|
private final String content;
|
||||||
|
private final String image;
|
||||||
|
private final LocalDateTime createdAt;
|
||||||
|
private final LocalDateTime modifiedAt;
|
||||||
|
private final List<TagResponse> tags;
|
||||||
|
|
||||||
|
private ArticleResponse(Long id, Long authorId, String title, String content, String image,
|
||||||
|
LocalDateTime createdAt, LocalDateTime modifiedAt,
|
||||||
|
List<TagResponse> tags) {
|
||||||
|
this.id = id;
|
||||||
|
this.authorId = authorId;
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.image = image;
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
this.modifiedAt = modifiedAt;
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.yam.app.article.presentation;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public final class TagResponse {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private TagResponse(Long id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TagResponse of(Long id, String name) {
|
||||||
|
return new TagResponse(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ public final class ArticleApiUri {
|
|||||||
|
|
||||||
public static final String WRITE_ARTICLE = "/api/articles/write";
|
public static final String WRITE_ARTICLE = "/api/articles/write";
|
||||||
public static final String FIND_ALL = "/api/articles/all";
|
public static final String FIND_ALL = "/api/articles/all";
|
||||||
|
public static final String FIND_BY_ID = "/api/articles/";
|
||||||
|
|
||||||
private ArticleApiUri() {}
|
private ArticleApiUri() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.yam.app.article.presentation;
|
||||||
|
|
||||||
|
import static com.yam.app.article.presentation.ArticleApiUri.FIND_BY_ID;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import com.yam.app.article.application.ArticleFacade;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
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.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
@DisplayName("Article Qurey HTTP API")
|
||||||
|
@WebMvcTest(ArticleQueryApi.class)
|
||||||
|
@ActiveProfiles("test")
|
||||||
|
class ArticleQueryApiTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
@MockBean
|
||||||
|
private ArticleFacade articleFacade;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("인증되지 않은 사용자가 게시글 조회 요청을 보냈다면 401에러를 반환한다.")
|
||||||
|
void unauthenticated_user_request() throws Exception {
|
||||||
|
//Act
|
||||||
|
final var actions = mockMvc.perform(get(FIND_BY_ID + 1)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
actions
|
||||||
|
.andExpect(status().isUnauthorized())
|
||||||
|
.andExpect(jsonPath("$.success").value(false))
|
||||||
|
.andExpect(jsonPath("$.data").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$.message").value("Unauthorized request"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user