diff --git a/src/main/java/io/spring/api/ArticlesApi.java b/src/main/java/io/spring/api/ArticlesApi.java index 56997b4..3f33cc1 100644 --- a/src/main/java/io/spring/api/ArticlesApi.java +++ b/src/main/java/io/spring/api/ArticlesApi.java @@ -44,6 +44,11 @@ public class ArticlesApi { throw new InvalidRequestException(bindingResult); } + if (articleQueryService.findBySlug(Article.toSlug(newArticleParam.getTitle()), null).isPresent()) { + bindingResult.rejectValue("title", "DUPLICATED", "article name exists"); + throw new InvalidRequestException(bindingResult); + } + Article article = new Article( newArticleParam.getTitle(), newArticleParam.getDescription(), diff --git a/src/main/java/io/spring/core/article/Article.java b/src/main/java/io/spring/core/article/Article.java index 65c021a..528430d 100644 --- a/src/main/java/io/spring/core/article/Article.java +++ b/src/main/java/io/spring/core/article/Article.java @@ -56,7 +56,7 @@ public class Article { this.updatedAt = new DateTime(); } - private String toSlug(String title) { + public static String toSlug(String title) { return title.toLowerCase().replaceAll("[\\&|[\\uFE30-\\uFFA0]|\\’|\\”|\\s\\?\\,\\.]+", "-"); } } diff --git a/src/test/java/io/spring/api/ArticlesApiTest.java b/src/test/java/io/spring/api/ArticlesApiTest.java index 2bb6c1e..dfe89b8 100644 --- a/src/test/java/io/spring/api/ArticlesApiTest.java +++ b/src/test/java/io/spring/api/ArticlesApiTest.java @@ -6,6 +6,7 @@ import io.spring.api.security.WebSecurityConfig; import io.spring.application.ArticleQueryService; import io.spring.application.data.ArticleData; import io.spring.application.data.ProfileData; +import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; import org.joda.time.DateTime; import org.junit.Before; @@ -24,6 +25,8 @@ import java.util.Optional; import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -68,6 +71,8 @@ public class ArticlesApiTest extends TestWithCurrentUser { Arrays.asList(tagList), new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false)); + when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any())).thenReturn(Optional.empty()); + when(articleQueryService.findById(any(), any())).thenReturn(Optional.of(articleData)); given() @@ -109,6 +114,43 @@ public class ArticlesApiTest extends TestWithCurrentUser { } + @Test + public void should_get_error_message_with_duplicated_title() { + String title = "How to train your dragon"; + String slug = "how-to-train-your-dragon"; + String description = "Ever wonder how?"; + String body = "You have to believe"; + String[] tagList = {"reactjs", "angularjs", "dragons"}; + Map param = prepareParam(title, description, body, tagList); + + ArticleData articleData = new ArticleData( + "123", + slug, + title, + description, + body, + false, + 0, + new DateTime(), + new DateTime(), + Arrays.asList(tagList), + new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false)); + + when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any())).thenReturn(Optional.of(articleData)); + + when(articleQueryService.findById(any(), any())).thenReturn(Optional.of(articleData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .post("/articles") + .then() + .statusCode(422); + + } + private HashMap prepareParam(final String title, final String description, final String body, final String[] tagList) { return new HashMap() {{ put("article", new HashMap() {{