add read one article
This commit is contained in:
parent
00f778c087
commit
bb08238bed
44
src/main/java/io/spring/JacksonCustomizations.java
Normal file
44
src/main/java/io/spring/JacksonCustomizations.java
Normal file
@ -0,0 +1,44 @@
|
||||
package io.spring;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import org.joda.time.DateTime;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Configuration
|
||||
public class JacksonCustomizations {
|
||||
|
||||
@Bean
|
||||
public Module realWorldModules() {
|
||||
return new RealWorldModules();
|
||||
}
|
||||
|
||||
public static class RealWorldModules extends SimpleModule {
|
||||
public RealWorldModules() {
|
||||
addSerializer(DateTime.class, new DateTimeSerializer());
|
||||
}
|
||||
}
|
||||
|
||||
public static class DateTimeSerializer extends StdSerializer<DateTime> {
|
||||
|
||||
protected DateTimeSerializer() {
|
||||
super(DateTime.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
if (value == null) {
|
||||
gen.writeNull();
|
||||
} else {
|
||||
gen.writeString(value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
src/main/java/io/spring/api/ArticleApi.java
Normal file
30
src/main/java/io/spring/api/ArticleApi.java
Normal file
@ -0,0 +1,30 @@
|
||||
package io.spring.api;
|
||||
|
||||
import io.spring.api.exception.ResourceNotFoundException;
|
||||
import io.spring.application.article.ArticleData;
|
||||
import io.spring.application.article.ArticleQueryService;
|
||||
import io.spring.core.user.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
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.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(path = "/articles/{slug}")
|
||||
public class ArticleApi {
|
||||
private ArticleQueryService articleQueryService;
|
||||
|
||||
@Autowired
|
||||
public ArticleApi(ArticleQueryService articleQueryService) {
|
||||
this.articleQueryService = articleQueryService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<ArticleData> article(@PathVariable("slug") String slug,
|
||||
@AuthenticationPrincipal User user) {
|
||||
return articleQueryService.findBySlug(slug, user).map(ResponseEntity::ok).orElseThrow(ResourceNotFoundException::new);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package io.spring.api.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND)
|
||||
public class ResourceNotFoundException extends RuntimeException {
|
||||
}
|
@ -26,6 +26,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(HttpMethod.POST, "/users", "/users/login").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/articles/**").permitAll()
|
||||
.anyRequest().authenticated();
|
||||
|
||||
http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
|
@ -23,17 +23,35 @@ public class ArticleQueryService {
|
||||
}
|
||||
|
||||
public Optional<ArticleData> findById(String id, User user) {
|
||||
ArticleData articleData = articleReadService.ofId(id);
|
||||
ArticleData articleData = articleReadService.findById(id);
|
||||
if (articleData == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
articleData.setFavorited(articleFavoritesQueryService.isUserFavorite(user.getId(), id));
|
||||
articleData.setFavoritesCount(articleFavoritesQueryService.articleFavoriteCount(id));
|
||||
articleData.getProfileData().setFollowing(
|
||||
userRelationshipQueryService.isUserFollowing(
|
||||
user.getId(),
|
||||
articleData.getProfileData().getId()));
|
||||
if (user != null) {
|
||||
fillExtraInfo(id, user, articleData);
|
||||
}
|
||||
return Optional.of(articleData);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<ArticleData> findBySlug(String slug, User user) {
|
||||
ArticleData articleData = articleReadService.findBySlug(slug);
|
||||
if (articleData == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
if (user != null) {
|
||||
fillExtraInfo(articleData.getId(), user, articleData);
|
||||
}
|
||||
return Optional.of(articleData);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillExtraInfo(String id, User user, ArticleData articleData) {
|
||||
articleData.setFavorited(articleFavoritesQueryService.isUserFavorite(user.getId(), id));
|
||||
articleData.setFavoritesCount(articleFavoritesQueryService.articleFavoriteCount(id));
|
||||
articleData.getProfileData().setFollowing(
|
||||
userRelationshipQueryService.isUserFollowing(
|
||||
user.getId(),
|
||||
articleData.getProfileData().getId()));
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,7 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
@Mapper
|
||||
public interface ArticleReadService {
|
||||
ArticleData ofId(@Param("id") String id);
|
||||
ArticleData findById(@Param("id") String id);
|
||||
|
||||
ArticleData findBySlug(@Param("slug") String slug);
|
||||
}
|
||||
|
@ -1,27 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="io.spring.application.article.ArticleReadService">
|
||||
<select id="ofId" resultMap="articleData">
|
||||
<sql id="selectArticleData">
|
||||
select
|
||||
A.id articleId,
|
||||
A.slug articleSlug,
|
||||
A.title articleTitle,
|
||||
A.description articleDescription,
|
||||
A.body articleBody,
|
||||
A.created_at articleCreatedAt,
|
||||
A.updated_at articleUpdatedAt,
|
||||
T.name as tagName,
|
||||
U.id userId,
|
||||
U.username userUsername,
|
||||
U.bio userBio,
|
||||
U.image userImage
|
||||
A.id articleId,
|
||||
A.slug articleSlug,
|
||||
A.title articleTitle,
|
||||
A.description articleDescription,
|
||||
A.body articleBody,
|
||||
A.created_at articleCreatedAt,
|
||||
A.updated_at articleUpdatedAt,
|
||||
T.name as tagName,
|
||||
U.id userId,
|
||||
U.username userUsername,
|
||||
U.bio userBio,
|
||||
U.image userImage
|
||||
from
|
||||
articles A
|
||||
left join article_tags AT on A.id = AT.article_id
|
||||
left join tags T on T.id = AT.article_id
|
||||
left join users U on U.id = A.user_id
|
||||
articles A
|
||||
left join article_tags AT on A.id = AT.article_id
|
||||
left join tags T on T.id = AT.article_id
|
||||
left join users U on U.id = A.user_id
|
||||
</sql>
|
||||
|
||||
<select id="findById" resultMap="articleData">
|
||||
<include refid="selectArticleData"/>
|
||||
where A.id = #{id}
|
||||
</select>
|
||||
<select id="findBySlug" resultMap="articleData">
|
||||
<include refid="selectArticleData"/>
|
||||
where A.slug = #{slug}
|
||||
</select>
|
||||
|
||||
<resultMap id="articleData" type="io.spring.application.article.ArticleData">
|
||||
<id column="articleId" property="id"/>
|
||||
|
@ -24,6 +24,7 @@ import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@ -131,4 +132,44 @@ public class ArticlesApiTest extends TestWithCurrentUser {
|
||||
}});
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_read_article_success() throws Exception {
|
||||
String slug = "test-new-article";
|
||||
Article article = new Article(slug, "Test New Article", "Desc", "Body", new String[]{"java", "spring", "jpg"}, user.getId());
|
||||
|
||||
DateTime time = new DateTime();
|
||||
ArticleData articleData = new ArticleData(
|
||||
article.getId(),
|
||||
article.getSlug(),
|
||||
article.getTitle(),
|
||||
article.getDescription(),
|
||||
article.getBody(),
|
||||
false,
|
||||
0,
|
||||
time,
|
||||
time,
|
||||
Arrays.asList("joda"),
|
||||
new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false));
|
||||
|
||||
when(articleQueryService.findBySlug(eq(slug), eq(null))).thenReturn(Optional.of(articleData));
|
||||
|
||||
RestAssured.when()
|
||||
.get("/articles/{slug}", slug)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("article.slug", equalTo(slug))
|
||||
.body("article.body", equalTo(articleData.getBody()))
|
||||
.body("article.createdAt", equalTo(time.toDateTimeISO().toString()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_404_if_article_not_found() throws Exception {
|
||||
when(articleQueryService.findBySlug(anyString(), any())).thenReturn(Optional.empty());
|
||||
RestAssured.when()
|
||||
.get("/articles/not-exists")
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user