read comments

This commit is contained in:
aisensiy 2017-08-15 17:52:23 +08:00
parent 445311ee1b
commit f31bcbc6e0
16 changed files with 116 additions and 45 deletions

View File

@ -22,6 +22,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping(path = "/articles/{slug}")
@ -36,13 +38,15 @@ public class ArticleApi {
}
@GetMapping
public ResponseEntity<ArticleData> article(@PathVariable("slug") String slug,
public ResponseEntity<?> article(@PathVariable("slug") String slug,
@AuthenticationPrincipal User user) {
return articleQueryService.findBySlug(slug, user).map(ResponseEntity::ok).orElseThrow(ResourceNotFoundException::new);
return articleQueryService.findBySlug(slug, user)
.map(articleData -> ResponseEntity.ok(articleResponse(articleData)))
.orElseThrow(ResourceNotFoundException::new);
}
@PutMapping
public ResponseEntity<ArticleData> updateArticle(@PathVariable("slug") String slug,
public ResponseEntity<?> updateArticle(@PathVariable("slug") String slug,
@AuthenticationPrincipal User user,
@Valid @RequestBody UpdateArticleParam updateArticleParam) {
return articleRepository.findBySlug(slug).map(article -> {
@ -54,7 +58,7 @@ public class ArticleApi {
updateArticleParam.getDescription(),
updateArticleParam.getBody());
articleRepository.save(article);
return ResponseEntity.ok(articleQueryService.findBySlug(slug, user).get());
return ResponseEntity.ok(articleResponse(articleQueryService.findBySlug(slug, user).get()));
}).orElseThrow(ResourceNotFoundException::new);
}
@ -69,6 +73,12 @@ public class ArticleApi {
return ResponseEntity.noContent().build();
}).orElseThrow(ResourceNotFoundException::new);
}
private Map<String, Object> articleResponse(ArticleData articleData) {
return new HashMap<String, Object>() {{
put("article", articleData);
}};
}
}
@Getter

View File

@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
@RestController
@RequestMapping(path = "/articles")
@ -47,7 +48,9 @@ public class ArticlesApi {
newArticleParam.getTagList(),
user.getId());
articleRepository.save(article);
return ResponseEntity.ok(articleQueryService.findById(article.getId(), user).get());
return ResponseEntity.ok(new HashMap<String, Object>() {{
put("article", articleQueryService.findById(article.getId(), user).get());
}});
}
}

View File

@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -25,6 +26,9 @@ import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.xml.ws.Response;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping(path = "/articles/{slug}/comments")
@ -43,7 +47,7 @@ public class CommentsApi {
}
@PostMapping
public ResponseEntity<CommentData> createComment(@PathVariable("slug") String slug,
public ResponseEntity<?> createComment(@PathVariable("slug") String slug,
@AuthenticationPrincipal User user,
@Valid @RequestBody NewCommentParam newCommentParam,
BindingResult bindingResult) {
@ -53,12 +57,28 @@ public class CommentsApi {
}
Comment comment = new Comment(newCommentParam.getBody(), user.getId(), article.getId());
commentRepository.save(comment);
return ResponseEntity.status(201).body(commentQueryService.findById(comment.getId(), user).get());
return ResponseEntity.status(201).body(commentResponse(commentQueryService.findById(comment.getId(), user).get()));
}
@GetMapping
public ResponseEntity getComments(@PathVariable("slug") String slug,
@AuthenticationPrincipal User user) {
Article article = findArticle(slug);
List<CommentData> comments = commentQueryService.findByArticleSlug(article.getSlug(), user);
return ResponseEntity.ok(new HashMap<String, Object>() {{
put("comments", comments);
}});
}
private Article findArticle(String slug) {
return articleRepository.findBySlug(slug).map(article -> article).orElseThrow(ResourceNotFoundException::new);
}
private Map<String, Object> commentResponse(CommentData commentData) {
return new HashMap<String, Object>() {{
put("comment", commentData);
}};
}
}
@Getter

View File

@ -2,6 +2,7 @@ package io.spring.api;
import com.fasterxml.jackson.annotation.JsonRootName;
import io.spring.application.user.UserQueryService;
import io.spring.application.user.UserWithToken;
import io.spring.core.user.User;
import io.spring.core.user.UserRepository;
import lombok.Getter;
@ -19,6 +20,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping(path = "/user")
@ -35,7 +38,7 @@ public class CurrentUserApi {
@GetMapping
public ResponseEntity currentUser(@AuthenticationPrincipal User currentUser,
@RequestHeader(value = "Authorization") String authorization) {
return ResponseEntity.ok(userQueryService.fetchCurrentUser(currentUser.getUsername(), authorization.split(" ")[1]));
return ResponseEntity.ok(userResponse(userQueryService.fetchCurrentUser(currentUser.getUsername(), authorization.split(" ")[1])));
}
@PutMapping
@ -50,7 +53,13 @@ public class CurrentUserApi {
updateUserParam.getBio(),
updateUserParam.getImage());
userRepository.save(currentUser);
return ResponseEntity.ok(userQueryService.fetchCurrentUser(currentUser.getUsername(), authorization.split(" ")[1]));
return ResponseEntity.ok(userResponse(userQueryService.fetchCurrentUser(currentUser.getUsername(), authorization.split(" ")[1])));
}
private Map<String, Object> userResponse(UserWithToken userWithToken) {
return new HashMap<String, Object>() {{
put("user", userWithToken);
}};
}
}

View File

@ -2,7 +2,9 @@ package io.spring.api;
import com.fasterxml.jackson.annotation.JsonRootName;
import io.spring.api.exception.InvalidRequestException;
import io.spring.application.user.UserData;
import io.spring.application.user.UserQueryService;
import io.spring.application.user.UserWithToken;
import io.spring.core.user.EncryptService;
import io.spring.core.user.User;
import io.spring.core.user.UserRepository;
@ -20,6 +22,8 @@ import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@ -63,19 +67,25 @@ public class UsersApi {
"",
defaultImage);
userRepository.save(user);
return ResponseEntity.status(201).body(userQueryService.fetchNewAuthenticatedUser(user.getUsername()));
return ResponseEntity.status(201).body(userResponse(userQueryService.fetchNewAuthenticatedUser(user.getUsername())));
}
@RequestMapping(path = "/users/login", method = POST)
public ResponseEntity userLogin(@Valid @RequestBody LoginParam loginParam, BindingResult bindingResult) {
Optional<User> optional = userRepository.findByEmail(loginParam.getEmail());
if (optional.isPresent() && encryptService.check(loginParam.getPassword(), optional.get().getPassword())) {
return ResponseEntity.ok(userQueryService.fetchNewAuthenticatedUser(optional.get().getUsername()));
return ResponseEntity.ok(userResponse(userQueryService.fetchNewAuthenticatedUser(optional.get().getUsername())));
} else {
bindingResult.rejectValue("password", "INVALID", "invalid email or password");
throw new InvalidRequestException(bindingResult);
}
}
private Map<String, Object> userResponse(UserWithToken userWithToken) {
return new HashMap<String, Object>() {{
put("user", userWithToken);
}};
}
}
@Getter

View File

@ -15,13 +15,14 @@ public class ErrorResourceSerializer extends JsonSerializer<ErrorResource> {
@Override
public void serialize(ErrorResource value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
Map<String, List<String>> json = new HashMap<>();
gen.writeStartObject();
gen.writeObjectFieldStart("errors");
for (FieldErrorResource fieldErrorResource : value.getFieldErrors()) {
if (!json.containsKey(fieldErrorResource.getField())) {
json.put(fieldErrorResource.getField(), new ArrayList<String>());
}
json.get(fieldErrorResource.getField()).add(fieldErrorResource.getMessage());
}
gen.writeStartObject();
for (Map.Entry<String, List<String>> pair : json.entrySet()) {
gen.writeArrayFieldStart(pair.getKey());
pair.getValue().forEach(content -> {
@ -34,5 +35,6 @@ public class ErrorResourceSerializer extends JsonSerializer<ErrorResource> {
gen.writeEndArray();
}
gen.writeEndObject();
gen.writeEndObject();
}
}

View File

@ -1,10 +1,8 @@
package io.spring.api.exception;
import com.fasterxml.jackson.annotation.JsonRootName;
import org.springframework.validation.Errors;
@SuppressWarnings("serial")
@JsonRootName("errors")
public class InvalidRequestException extends RuntimeException {
private final Errors errors;

View File

@ -13,7 +13,6 @@ import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonRootName("article")
public class ArticleData {
private String id;
private String slug;

View File

@ -1,7 +1,7 @@
package io.spring.application.comment;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import io.spring.application.profile.ProfileData;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -11,10 +11,10 @@ import org.joda.time.DateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonRootName("comment")
public class CommentData {
private String id;
private String body;
@JsonIgnore
private String articleId;
private DateTime createdAt;
private DateTime updatedAt;

View File

@ -4,6 +4,8 @@ import io.spring.application.profile.UserRelationshipQueryService;
import io.spring.core.user.User;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@ -28,4 +30,8 @@ public class CommentQueryService {
}
return Optional.ofNullable(commentData);
}
public List<CommentData> findByArticleSlug(String slug, User user) {
return new ArrayList<>();
}
}

View File

@ -9,7 +9,6 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonRootName("user")
public class UserData {
private String id;
private String email;

View File

@ -25,21 +25,3 @@ public class UserQueryService {
}
}
@JsonRootName("user")
@Getter
class UserWithToken {
private String email;
private String username;
private String bio;
private String image;
private String token;
public UserWithToken(UserData userData, String token) {
this.email = userData.getEmail();
this.username = userData.getUsername();
this.bio = userData.getBio();
this.image = userData.getImage();
this.token = token;
}
}

View File

@ -0,0 +1,21 @@
package io.spring.application.user;
import lombok.Getter;
@Getter
public class UserWithToken {
private String email;
private String username;
private String bio;
private String image;
private String token;
public UserWithToken(UserData userData, String token) {
this.email = userData.getEmail();
this.username = userData.getUsername();
this.bio = userData.getBio();
this.image = userData.getImage();
this.token = token;
}
}

View File

@ -1,5 +1,4 @@
spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true
spring.jackson.serialization.WRAP_ROOT_VALUE=true
image.default=https://static.productionready.io/images/smiley-cyrus.jpg
jwt.secret=nRvyYC4soFxBdZ-F-5Nnzz5USXstR1YylsTd-mA0aKtI9HUlriGrtkf-TiuDapkLiUCogO3JOK7kwZisrHp6wA
jwt.sessionTime=86400

View File

@ -111,6 +111,7 @@ public class ArticlesApiTest extends TestWithCurrentUser {
.body(param)
.when()
.post("/articles")
.prettyPeek()
.then()
.statusCode(422)
.body("errors.body[0]", equalTo("can't be empty"));

View File

@ -16,6 +16,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@ -47,6 +48,7 @@ public class CommentsApiTest extends TestWithCurrentUser {
private CommentQueryService commentQueryService;
private Article article;
private CommentData commentData;
@Before
public void setUp() throws Exception {
@ -58,6 +60,13 @@ public class CommentsApiTest extends TestWithCurrentUser {
article = new Article("title", "desc", "body", new String[]{"test", "java"}, user.getId());
when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article));
commentData = new CommentData(
"123",
"comment",
article.getId(),
new DateTime(),
new DateTime(),
new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false));
}
@Test
@ -68,14 +77,6 @@ public class CommentsApiTest extends TestWithCurrentUser {
}});
}};
CommentData commentData = new CommentData(
"123",
"comment",
article.getId(),
new DateTime(),
new DateTime(),
new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false));
when(commentQueryService.findById(anyString(), eq(user))).thenReturn(Optional.of(commentData));
given()
@ -108,4 +109,15 @@ public class CommentsApiTest extends TestWithCurrentUser {
.body("errors.body[0]", equalTo("can't be empty"));
}
@Test
public void should_get_comments_of_article_success() throws Exception {
when(commentQueryService.findByArticleSlug(anyString(), eq(null))).thenReturn(Arrays.asList(commentData));
RestAssured.when()
.get("/articles/{slug}/comments", article.getSlug())
.prettyPeek()
.then()
.statusCode(200)
.body("comments[0].id", equalTo(commentData.getId()));
}
}