From 01fac42c64872837e204fda64dedde241cd8128e Mon Sep 17 00:00:00 2001 From: xushanchuan Date: Fri, 19 Mar 2021 15:23:37 +0800 Subject: [PATCH] refactor: application level prepare for graphql and switch to sqlite --- .gitignore | 3 +- build.gradle | 11 +- .../java/io/spring/RealworldApplication.java | 9 +- .../api/security/WebSecurityConfig.java | 108 ++++---- .../application/ArticleQueryService.java | 241 +++++++++++------- .../application/CommentQueryService.java | 102 +++++--- .../application/CursorPageParameter.java | 44 ++++ .../io/spring/application/CursorPager.java | 44 ++++ src/main/java/io/spring/application/Node.java | 5 + src/main/java/io/spring/application/Page.java | 36 ++- .../spring/application/data/ArticleData.java | 36 +-- .../spring/application/data/CommentData.java | 24 +- .../readservice/ArticleReadService.java | 35 ++- .../readservice/CommentReadService.java | 11 +- .../resources/application-test.properties | 1 + src/main/resources/application.properties | 5 + .../resources/mapper/ArticleReadService.xml | 70 ++++- .../resources/mapper/CommentReadService.xml | 18 ++ .../article/ArticleQueryServiceTest.java | 45 +++- .../comment/CommentQueryServiceTest.java | 8 +- .../profile/ProfileQueryServiceTest.java | 40 ++- .../application/tag/TagsQueryServiceTest.java | 8 +- .../io/spring/infrastructure/DbTestBase.java | 14 + .../ArticleRepositoryTransactionTest.java | 6 +- .../article/MyBatisArticleRepositoryTest.java | 8 +- .../comment/MyBatisCommentRepositoryTest.java | 46 ++-- .../MyBatisArticleFavoriteRepositoryTest.java | 58 ++--- .../user/MyBatisUserRepositoryTest.java | 126 +++++---- 28 files changed, 746 insertions(+), 416 deletions(-) create mode 100644 src/main/java/io/spring/application/CursorPageParameter.java create mode 100644 src/main/java/io/spring/application/CursorPager.java create mode 100644 src/main/java/io/spring/application/Node.java create mode 100644 src/main/resources/application-test.properties create mode 100644 src/test/java/io/spring/infrastructure/DbTestBase.java diff --git a/.gitignore b/.gitignore index c9f7010..3deb366 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .gradle /build/ !gradle/wrapper/gradle-wrapper.jar +*.db ### STS ### .apt_generated @@ -22,4 +23,4 @@ build/ nbbuild/ dist/ nbdist/ -.nb-gradle/ \ No newline at end of file +.nb-gradle/ diff --git a/build.gradle b/build.gradle index 738b79d..12781f0 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ plugins { id 'java' id 'idea' id 'eclipse' + id "com.netflix.dgs.codegen" version "4.2.0" } version = '0.0.1-SNAPSHOT' @@ -13,6 +14,7 @@ targetCompatibility = JavaVersion.VERSION_1_8 repositories { mavenLocal() mavenCentral() + jcenter() } configurations { @@ -26,11 +28,11 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-hateoas' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3' + implementation "com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter:latest.release" implementation 'org.flywaydb:flyway-core' implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'joda-time:joda-time:2.10.6' - - runtimeOnly 'com.h2database:h2' + implementation 'org.xerial:sqlite-jdbc:3.34.0' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' @@ -47,3 +49,8 @@ dependencies { test { useJUnitPlatform() } + +generateJava { + schemaPaths = ["${projectDir}/src/main/resources/schema"] // List of directories containing schema files + packageName = 'io.spring.graphql' // The package name to use to generate sources +} \ No newline at end of file diff --git a/src/main/java/io/spring/RealworldApplication.java b/src/main/java/io/spring/RealworldApplication.java index 65ea98f..69af182 100644 --- a/src/main/java/io/spring/RealworldApplication.java +++ b/src/main/java/io/spring/RealworldApplication.java @@ -2,11 +2,12 @@ package io.spring; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -@SpringBootApplication +@SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class) public class RealworldApplication { - public static void main(String[] args) { - SpringApplication.run(RealworldApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(RealworldApplication.class, args); + } } diff --git a/src/main/java/io/spring/api/security/WebSecurityConfig.java b/src/main/java/io/spring/api/security/WebSecurityConfig.java index fa52492..7d30e1d 100644 --- a/src/main/java/io/spring/api/security/WebSecurityConfig.java +++ b/src/main/java/io/spring/api/security/WebSecurityConfig.java @@ -21,56 +21,70 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - @Value("${spring.h2.console.enabled:false}") - private boolean h2ConsoleEnabled; + @Value("${spring.h2.console.enabled:false}") + private boolean h2ConsoleEnabled; - @Bean - public JwtTokenFilter jwtTokenFilter() { - return new JwtTokenFilter(); + @Bean + public JwtTokenFilter jwtTokenFilter() { + return new JwtTokenFilter(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + + if (h2ConsoleEnabled) { + http.authorizeRequests() + .antMatchers("/h2-console", "/h2-console/**") + .permitAll() + .and() + .headers() + .frameOptions() + .sameOrigin(); } - @Override - protected void configure(HttpSecurity http) throws Exception { + http.csrf() + .disable() + .cors() + .and() + .exceptionHandling() + .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers(HttpMethod.OPTIONS) + .permitAll() + .antMatchers("/graphiql") + .permitAll() + .antMatchers("/graphql") + .permitAll() + .antMatchers(HttpMethod.GET, "/articles/feed") + .authenticated() + .antMatchers(HttpMethod.POST, "/users", "/users/login") + .permitAll() + .antMatchers(HttpMethod.GET, "/articles/**", "/profiles/**", "/tags") + .permitAll() + .anyRequest() + .authenticated(); - if (h2ConsoleEnabled) { - http.authorizeRequests() - .antMatchers("/h2-console", "/h2-console/**").permitAll() - .and() - .headers().frameOptions().sameOrigin(); - } + http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + } - http.csrf().disable() - .cors() - .and() - .exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) - .and() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS).permitAll() - .antMatchers("/graphiql").permitAll() - .antMatchers("/graphql").permitAll() - .antMatchers(HttpMethod.GET, "/articles/feed").authenticated() - .antMatchers(HttpMethod.POST, "/users", "/users/login").permitAll() - .antMatchers(HttpMethod.GET, "/articles/**", "/profiles/**", "/tags").permitAll() - .anyRequest().authenticated(); - - http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - final CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(asList("*")); - configuration.setAllowedMethods(asList("HEAD", - "GET", "POST", "PUT", "DELETE", "PATCH")); - // setAllowCredentials(true) is important, otherwise: - // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. - configuration.setAllowCredentials(false); - // setAllowedHeaders is important! Without it, OPTIONS preflight request - // will fail with 403 Invalid CORS request - configuration.setAllowedHeaders(asList("Authorization", "Cache-Control", "Content-Type")); - final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } + @Bean + public CorsConfigurationSource corsConfigurationSource() { + final CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(asList("*")); + configuration.setAllowedMethods(asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); + // setAllowCredentials(true) is important, otherwise: + // The value of the 'Access-Control-Allow-Origin' header in the response must not be the + // wildcard '*' when the request's credentials mode is 'include'. + configuration.setAllowCredentials(false); + // setAllowedHeaders is important! Without it, OPTIONS preflight request + // will fail with 403 Invalid CORS request + configuration.setAllowedHeaders(asList("Authorization", "Cache-Control", "Content-Type")); + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } } diff --git a/src/main/java/io/spring/application/ArticleQueryService.java b/src/main/java/io/spring/application/ArticleQueryService.java index 0e0b959..cb621af 100644 --- a/src/main/java/io/spring/application/ArticleQueryService.java +++ b/src/main/java/io/spring/application/ArticleQueryService.java @@ -1,5 +1,7 @@ package io.spring.application; +import static java.util.stream.Collectors.toList; + import io.spring.application.data.ArticleData; import io.spring.application.data.ArticleDataList; import io.spring.application.data.ArticleFavoriteCount; @@ -7,126 +9,179 @@ import io.spring.core.user.User; import io.spring.infrastructure.mybatis.readservice.ArticleFavoritesReadService; import io.spring.infrastructure.mybatis.readservice.ArticleReadService; import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; - -import static java.util.stream.Collectors.toList; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; @Service public class ArticleQueryService { - private ArticleReadService articleReadService; - private UserRelationshipQueryService userRelationshipQueryService; - private ArticleFavoritesReadService articleFavoritesReadService; + private ArticleReadService articleReadService; + private UserRelationshipQueryService userRelationshipQueryService; + private ArticleFavoritesReadService articleFavoritesReadService; - @Autowired - public ArticleQueryService(ArticleReadService articleReadService, - UserRelationshipQueryService userRelationshipQueryService, - ArticleFavoritesReadService articleFavoritesReadService) { - this.articleReadService = articleReadService; - this.userRelationshipQueryService = userRelationshipQueryService; - this.articleFavoritesReadService = articleFavoritesReadService; + @Autowired + public ArticleQueryService( + ArticleReadService articleReadService, + UserRelationshipQueryService userRelationshipQueryService, + ArticleFavoritesReadService articleFavoritesReadService) { + this.articleReadService = articleReadService; + this.userRelationshipQueryService = userRelationshipQueryService; + this.articleFavoritesReadService = articleFavoritesReadService; + } + + public Optional findById(String id, User user) { + ArticleData articleData = articleReadService.findById(id); + if (articleData == null) { + return Optional.empty(); + } else { + if (user != null) { + fillExtraInfo(id, user, articleData); + } + return Optional.of(articleData); } + } - public Optional findById(String id, User user) { - ArticleData articleData = articleReadService.findById(id); - if (articleData == null) { - return Optional.empty(); - } else { - if (user != null) { - fillExtraInfo(id, user, articleData); - } - return Optional.of(articleData); - } + public Optional 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); } + } - public Optional 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); - } + public CursorPager findRecentArticlesWithCursor( + String tag, String author, String favoritedBy, CursorPageParameter page, User currentUser) { + List articleIds = + articleReadService.findArticlesWithCursor(tag, author, favoritedBy, page); + if (articleIds.size() == 0) { + return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); + } else { + boolean hasExtra = articleIds.size() > page.getLimit(); + if (hasExtra) { + articleIds.remove(page.getLimit()); + } + if (!page.isNext()) { + Collections.reverse(articleIds); + } + + List articles = articleReadService.findArticles(articleIds); + fillExtraInfo(articles, currentUser); + + return new CursorPager<>(articles, page.getDirection(), hasExtra); } + } - public ArticleDataList findRecentArticles(String tag, String author, String favoritedBy, Page page, User currentUser) { - List articleIds = articleReadService.queryArticles(tag, author, favoritedBy, page); - int articleCount = articleReadService.countArticle(tag, author, favoritedBy); - if (articleIds.size() == 0) { - return new ArticleDataList(new ArrayList<>(), articleCount); - } else { - List articles = articleReadService.findArticles(articleIds); - fillExtraInfo(articles, currentUser); - return new ArticleDataList(articles, articleCount); - } + public CursorPager findUserFeedWithCursor(User user, CursorPageParameter page) { + List followdUsers = userRelationshipQueryService.followedUsers(user.getId()); + if (followdUsers.size() == 0) { + return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); + } else { + List articles = + articleReadService.findArticlesOfAuthorsWithCursor(followdUsers, page); + boolean hasExtra = articles.size() > page.getLimit(); + if (hasExtra) { + articles.remove(page.getLimit()); + } + if (!page.isNext()) { + Collections.reverse(articles); + } + fillExtraInfo(articles, user); + return new CursorPager<>(articles, page.getDirection(), hasExtra); } + } - private void fillExtraInfo(List articles, User currentUser) { - setFavoriteCount(articles); - if (currentUser != null) { - setIsFavorite(articles, currentUser); - setIsFollowingAuthor(articles, currentUser); - } + public ArticleDataList findRecentArticles( + String tag, String author, String favoritedBy, Page page, User currentUser) { + List articleIds = articleReadService.queryArticles(tag, author, favoritedBy, page); + int articleCount = articleReadService.countArticle(tag, author, favoritedBy); + if (articleIds.size() == 0) { + return new ArticleDataList(new ArrayList<>(), articleCount); + } else { + List articles = articleReadService.findArticles(articleIds); + fillExtraInfo(articles, currentUser); + return new ArticleDataList(articles, articleCount); } + } - private void setIsFollowingAuthor(List articles, User currentUser) { - Set followingAuthors = userRelationshipQueryService.followingAuthors( + public ArticleDataList findUserFeed(User user, Page page) { + List followdUsers = userRelationshipQueryService.followedUsers(user.getId()); + if (followdUsers.size() == 0) { + return new ArticleDataList(new ArrayList<>(), 0); + } else { + List articles = articleReadService.findArticlesOfAuthors(followdUsers, page); + fillExtraInfo(articles, user); + int count = articleReadService.countFeedSize(followdUsers); + return new ArticleDataList(articles, count); + } + } + + private void fillExtraInfo(List articles, User currentUser) { + setFavoriteCount(articles); + if (currentUser != null) { + setIsFavorite(articles, currentUser); + setIsFollowingAuthor(articles, currentUser); + } + } + + private void setIsFollowingAuthor(List articles, User currentUser) { + Set followingAuthors = + userRelationshipQueryService.followingAuthors( currentUser.getId(), - articles.stream().map(articleData1 -> articleData1.getProfileData().getId()).collect(toList())); - articles.forEach(articleData -> { - if (followingAuthors.contains(articleData.getProfileData().getId())) { - articleData.getProfileData().setFollowing(true); - } + articles.stream() + .map(articleData1 -> articleData1.getProfileData().getId()) + .collect(toList())); + articles.forEach( + articleData -> { + if (followingAuthors.contains(articleData.getProfileData().getId())) { + articleData.getProfileData().setFollowing(true); + } }); - } + } - private void setFavoriteCount(List articles) { - List favoritesCounts = articleFavoritesReadService.articlesFavoriteCount(articles.stream().map(ArticleData::getId).collect(toList())); - Map countMap = new HashMap<>(); - favoritesCounts.forEach(item -> { - countMap.put(item.getId(), item.getCount()); + private void setFavoriteCount(List articles) { + List favoritesCounts = + articleFavoritesReadService.articlesFavoriteCount( + articles.stream().map(ArticleData::getId).collect(toList())); + Map countMap = new HashMap<>(); + favoritesCounts.forEach( + item -> { + countMap.put(item.getId(), item.getCount()); }); - articles.forEach(articleData -> articleData.setFavoritesCount(countMap.get(articleData.getId()))); - } + articles.forEach( + articleData -> articleData.setFavoritesCount(countMap.get(articleData.getId()))); + } - private void setIsFavorite(List articles, User currentUser) { - Set favoritedArticles = articleFavoritesReadService.userFavorites(articles.stream().map(articleData -> articleData.getId()).collect(toList()), currentUser); + private void setIsFavorite(List articles, User currentUser) { + Set favoritedArticles = + articleFavoritesReadService.userFavorites( + articles.stream().map(articleData -> articleData.getId()).collect(toList()), + currentUser); - articles.forEach(articleData -> { - if (favoritedArticles.contains(articleData.getId())) { - articleData.setFavorited(true); - } + articles.forEach( + articleData -> { + if (favoritedArticles.contains(articleData.getId())) { + articleData.setFavorited(true); + } }); - } + } - private void fillExtraInfo(String id, User user, ArticleData articleData) { - articleData.setFavorited(articleFavoritesReadService.isUserFavorite(user.getId(), id)); - articleData.setFavoritesCount(articleFavoritesReadService.articleFavoriteCount(id)); - articleData.getProfileData().setFollowing( + private void fillExtraInfo(String id, User user, ArticleData articleData) { + articleData.setFavorited(articleFavoritesReadService.isUserFavorite(user.getId(), id)); + articleData.setFavoritesCount(articleFavoritesReadService.articleFavoriteCount(id)); + articleData + .getProfileData() + .setFollowing( userRelationshipQueryService.isUserFollowing( - user.getId(), - articleData.getProfileData().getId())); - } - - public ArticleDataList findUserFeed(User user, Page page) { - List followdUsers = userRelationshipQueryService.followedUsers(user.getId()); - if (followdUsers.size() == 0) { - return new ArticleDataList(new ArrayList<>(), 0); - } else { - List articles = articleReadService.findArticlesOfAuthors(followdUsers, page); - fillExtraInfo(articles, user); - int count = articleReadService.countFeedSize(followdUsers); - return new ArticleDataList(articles, count); - } - } + user.getId(), articleData.getProfileData().getId())); + } } - diff --git a/src/main/java/io/spring/application/CommentQueryService.java b/src/main/java/io/spring/application/CommentQueryService.java index f400b53..ab0d10a 100644 --- a/src/main/java/io/spring/application/CommentQueryService.java +++ b/src/main/java/io/spring/application/CommentQueryService.java @@ -1,49 +1,89 @@ package io.spring.application; import io.spring.application.data.CommentData; -import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; import io.spring.core.user.User; import io.spring.infrastructure.mybatis.readservice.CommentReadService; -import org.springframework.stereotype.Service; - +import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.springframework.stereotype.Service; @Service public class CommentQueryService { - private CommentReadService commentReadService; - private UserRelationshipQueryService userRelationshipQueryService; + private CommentReadService commentReadService; + private UserRelationshipQueryService userRelationshipQueryService; - public CommentQueryService(CommentReadService commentReadService, UserRelationshipQueryService userRelationshipQueryService) { - this.commentReadService = commentReadService; - this.userRelationshipQueryService = userRelationshipQueryService; - } + public CommentQueryService( + CommentReadService commentReadService, + UserRelationshipQueryService userRelationshipQueryService) { + this.commentReadService = commentReadService; + this.userRelationshipQueryService = userRelationshipQueryService; + } - public Optional findById(String id, User user) { - CommentData commentData = commentReadService.findById(id); - if (commentData == null) { - return Optional.empty(); - } else { - commentData.getProfileData().setFollowing( - userRelationshipQueryService.isUserFollowing( - user.getId(), - commentData.getProfileData().getId())); - } - return Optional.ofNullable(commentData); + public Optional findById(String id, User user) { + CommentData commentData = commentReadService.findById(id); + if (commentData == null) { + return Optional.empty(); + } else { + commentData + .getProfileData() + .setFollowing( + userRelationshipQueryService.isUserFollowing( + user.getId(), commentData.getProfileData().getId())); } + return Optional.ofNullable(commentData); + } - public List findByArticleId(String articleId, User user) { - List comments = commentReadService.findByArticleId(articleId); - if (comments.size() > 0 && user != null) { - Set followingAuthors = userRelationshipQueryService.followingAuthors(user.getId(), comments.stream().map(commentData -> commentData.getProfileData().getId()).collect(Collectors.toList())); - comments.forEach(commentData -> { - if (followingAuthors.contains(commentData.getProfileData().getId())) { - commentData.getProfileData().setFollowing(true); - } - }); - } - return comments; + public List findByArticleId(String articleId, User user) { + List comments = commentReadService.findByArticleId(articleId); + if (comments.size() > 0 && user != null) { + Set followingAuthors = + userRelationshipQueryService.followingAuthors( + user.getId(), + comments.stream() + .map(commentData -> commentData.getProfileData().getId()) + .collect(Collectors.toList())); + comments.forEach( + commentData -> { + if (followingAuthors.contains(commentData.getProfileData().getId())) { + commentData.getProfileData().setFollowing(true); + } + }); } + return comments; + } + + public CursorPager findByArticleIdWithCursor( + String articleId, User user, CursorPageParameter page) { + List comments = commentReadService.findByArticleIdWithCursor(articleId, page); + if (comments.isEmpty()) { + return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); + } + if (user != null) { + Set followingAuthors = + userRelationshipQueryService.followingAuthors( + user.getId(), + comments.stream() + .map(commentData -> commentData.getProfileData().getId()) + .collect(Collectors.toList())); + comments.forEach( + commentData -> { + if (followingAuthors.contains(commentData.getProfileData().getId())) { + commentData.getProfileData().setFollowing(true); + } + }); + } + boolean hasExtra = comments.size() > page.getLimit(); + if (hasExtra) { + comments.remove(page.getLimit()); + } + if (!page.isNext()) { + Collections.reverse(comments); + } + return new CursorPager<>(comments, page.getDirection(), hasExtra); + } } diff --git a/src/main/java/io/spring/application/CursorPageParameter.java b/src/main/java/io/spring/application/CursorPageParameter.java new file mode 100644 index 0000000..d9145a4 --- /dev/null +++ b/src/main/java/io/spring/application/CursorPageParameter.java @@ -0,0 +1,44 @@ +package io.spring.application; + +import io.spring.application.CursorPager.Direction; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class CursorPageParameter { + private static final int MAX_LIMIT = 1000; + private int limit = 20; + private String cursor; + private Direction direction; + + public CursorPageParameter(String cursor, int limit, Direction direction) { + setLimit(limit); + setCursor(cursor); + setDirection(direction); + } + + public boolean isNext() { + return direction == Direction.NEXT; + } + + public int getQueryLimit() { + return limit + 1; + } + + private void setCursor(String cursor) { + if (cursor == null) { + this.cursor = ""; + } else { + this.cursor = cursor; + } + } + + private void setLimit(int limit) { + if (limit > MAX_LIMIT) { + this.limit = MAX_LIMIT; + } else if (limit > 0) { + this.limit = limit; + } + } +} diff --git a/src/main/java/io/spring/application/CursorPager.java b/src/main/java/io/spring/application/CursorPager.java new file mode 100644 index 0000000..cff42d7 --- /dev/null +++ b/src/main/java/io/spring/application/CursorPager.java @@ -0,0 +1,44 @@ +package io.spring.application; + +import java.util.List; +import lombok.Getter; + +@Getter +public class CursorPager { + private List data; + private boolean next; + private boolean previous; + + public CursorPager(List data, Direction direction, boolean hasExtra) { + this.data = data; + + if (direction == Direction.NEXT) { + this.previous = false; + this.next = hasExtra; + } else { + this.next = false; + this.previous = hasExtra; + } + } + + public boolean hasNext() { + return next; + } + + public boolean hasPrevious() { + return previous; + } + + public String getStartCursor() { + return data.isEmpty() ? "" : data.get(0).getCursor(); + } + + public String getEndCursor() { + return data.isEmpty() ? "" : data.get(data.size() - 1).getCursor(); + } + + public enum Direction { + PREV, + NEXT + } +} diff --git a/src/main/java/io/spring/application/Node.java b/src/main/java/io/spring/application/Node.java new file mode 100644 index 0000000..780982e --- /dev/null +++ b/src/main/java/io/spring/application/Node.java @@ -0,0 +1,5 @@ +package io.spring.application; + +public interface Node { + String getCursor(); +} diff --git a/src/main/java/io/spring/application/Page.java b/src/main/java/io/spring/application/Page.java index e5bf6fb..d273e99 100644 --- a/src/main/java/io/spring/application/Page.java +++ b/src/main/java/io/spring/application/Page.java @@ -1,33 +1,31 @@ package io.spring.application; import lombok.Data; -import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor @Data -@Getter public class Page { - private static final int MAX_LIMIT = 100; - private int offset = 0; - private int limit = 20; + private static final int MAX_LIMIT = 100; + private int offset = 0; + private int limit = 20; - public Page(int offset, int limit) { - setOffset(offset); - setLimit(limit); - } + public Page(int offset, int limit) { + setOffset(offset); + setLimit(limit); + } - private void setOffset(int offset) { - if (offset > 0) { - this.offset = offset; - } + private void setOffset(int offset) { + if (offset > 0) { + this.offset = offset; } + } - private void setLimit(int limit) { - if (limit > MAX_LIMIT) { - this.limit = MAX_LIMIT; - } else if (limit > 0) { - this.limit = limit; - } + private void setLimit(int limit) { + if (limit > MAX_LIMIT) { + this.limit = MAX_LIMIT; + } else if (limit > 0) { + this.limit = limit; } + } } diff --git a/src/main/java/io/spring/application/data/ArticleData.java b/src/main/java/io/spring/application/data/ArticleData.java index 9e15c89..14ac3d6 100644 --- a/src/main/java/io/spring/application/data/ArticleData.java +++ b/src/main/java/io/spring/application/data/ArticleData.java @@ -1,28 +1,32 @@ package io.spring.application.data; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.joda.time.DateTime; -import java.util.List; - @Data @NoArgsConstructor @AllArgsConstructor -public class ArticleData { - private String id; - private String slug; - private String title; - private String description; - private String body; - private boolean favorited; - private int favoritesCount; - private DateTime createdAt; - private DateTime updatedAt; - private List tagList; - @JsonProperty("author") - private ProfileData profileData; -} +public class ArticleData implements io.spring.application.Node { + private String id; + private String slug; + private String title; + private String description; + private String body; + private boolean favorited; + private int favoritesCount; + private DateTime createdAt; + private DateTime updatedAt; + private List tagList; + @JsonProperty("author") + private ProfileData profileData; + + @Override + public String getCursor() { + return String.valueOf(getUpdatedAt().getMillis()); + } +} diff --git a/src/main/java/io/spring/application/data/CommentData.java b/src/main/java/io/spring/application/data/CommentData.java index d2cdab5..568649d 100644 --- a/src/main/java/io/spring/application/data/CommentData.java +++ b/src/main/java/io/spring/application/data/CommentData.java @@ -2,6 +2,7 @@ package io.spring.application.data; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import io.spring.application.Node; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,13 +11,18 @@ import org.joda.time.DateTime; @Data @NoArgsConstructor @AllArgsConstructor -public class CommentData { - private String id; - private String body; - @JsonIgnore - private String articleId; - private DateTime createdAt; - private DateTime updatedAt; - @JsonProperty("author") - private ProfileData profileData; +public class CommentData implements Node { + private String id; + private String body; + @JsonIgnore private String articleId; + private DateTime createdAt; + private DateTime updatedAt; + + @JsonProperty("author") + private ProfileData profileData; + + @Override + public String getCursor() { + return String.valueOf(createdAt.getMillis()); + } } diff --git a/src/main/java/io/spring/infrastructure/mybatis/readservice/ArticleReadService.java b/src/main/java/io/spring/infrastructure/mybatis/readservice/ArticleReadService.java index 09dd572..3075a3d 100644 --- a/src/main/java/io/spring/infrastructure/mybatis/readservice/ArticleReadService.java +++ b/src/main/java/io/spring/infrastructure/mybatis/readservice/ArticleReadService.java @@ -1,25 +1,42 @@ package io.spring.infrastructure.mybatis.readservice; +import io.spring.application.CursorPageParameter; import io.spring.application.Page; import io.spring.application.data.ArticleData; +import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.util.List; - @Mapper public interface ArticleReadService { - ArticleData findById(@Param("id") String id); + ArticleData findById(@Param("id") String id); - ArticleData findBySlug(@Param("slug") String slug); + ArticleData findBySlug(@Param("slug") String slug); - List queryArticles(@Param("tag") String tag, @Param("author") String author, @Param("favoritedBy") String favoritedBy, @Param("page") Page page); + List queryArticles( + @Param("tag") String tag, + @Param("author") String author, + @Param("favoritedBy") String favoritedBy, + @Param("page") Page page); - int countArticle(@Param("tag") String tag, @Param("author") String author, @Param("favoritedBy") String favoritedBy); + int countArticle( + @Param("tag") String tag, + @Param("author") String author, + @Param("favoritedBy") String favoritedBy); - List findArticles(@Param("articleIds") List articleIds); + List findArticles(@Param("articleIds") List articleIds); - List findArticlesOfAuthors(@Param("authors") List authors, @Param("page") Page page); + List findArticlesOfAuthors( + @Param("authors") List authors, @Param("page") Page page); - int countFeedSize(@Param("authors") List authors); + List findArticlesOfAuthorsWithCursor( + @Param("authors") List authors, @Param("page") CursorPageParameter page); + + int countFeedSize(@Param("authors") List authors); + + List findArticlesWithCursor( + @Param("tag") String tag, + @Param("author") String author, + @Param("favoritedBy") String favoritedBy, + @Param("page") CursorPageParameter page); } diff --git a/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java b/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java index 50c96a9..aaeb547 100644 --- a/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java +++ b/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java @@ -1,14 +1,17 @@ package io.spring.infrastructure.mybatis.readservice; +import io.spring.application.CursorPageParameter; import io.spring.application.data.CommentData; +import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.util.List; - @Mapper public interface CommentReadService { - CommentData findById(@Param("id") String id); + CommentData findById(@Param("id") String id); - List findByArticleId(@Param("articleId") String articleId); + List findByArticleId(@Param("articleId") String articleId); + + List findByArticleIdWithCursor( + @Param("articleId") String articleId, @Param("page") CursorPageParameter page); } diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties new file mode 100644 index 0000000..0902f5c --- /dev/null +++ b/src/main/resources/application-test.properties @@ -0,0 +1 @@ +spring.datasource.url=jdbc:sqlite::memory: \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8fea8af..2d6e13c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,7 @@ +spring.datasource.url=jdbc:sqlite:dev.db +spring.datasource.driver-class-name=org.sqlite.JDBC +spring.datasource.username= +spring.datasource.password= spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true image.default=https://static.productionready.io/images/smiley-cyrus.jpg jwt.secret=nRvyYC4soFxBdZ-F-5Nnzz5USXstR1YylsTd-mA0aKtI9HUlriGrtkf-TiuDapkLiUCogO3JOK7kwZisrHp6wA @@ -9,5 +13,6 @@ mybatis.configuration.use-generated-keys=true mybatis.type-handlers-package=io.spring.infrastructure.mybatis mybatis.mapper-locations=mapper/*.xml logging.level.io.spring.infrastructure.mybatis.readservice.ArticleReadService=DEBUG +logging.level.io.spring.infrastructure.mybatis.mapper=DEBUG # Uncomment the following line to enable and allow access to the h2-console #spring.h2.console.enabled=true diff --git a/src/main/resources/mapper/ArticleReadService.xml b/src/main/resources/mapper/ArticleReadService.xml index d7819a3..7fee66d 100644 --- a/src/main/resources/mapper/ArticleReadService.xml +++ b/src/main/resources/mapper/ArticleReadService.xml @@ -24,6 +24,17 @@ left join tags T on T.id = AT.tag_id left join users U on U.id = A.user_id + + select + DISTINCT(A.id) articleId, A.created_at + from + articles A + left join article_tags AT on A.id = AT.article_id + left join tags T on T.id = AT.tag_id + left join article_favorites AF on AF.article_id = A.id + left join users AU on AU.id = A.user_id + left join users AFU on AFU.id = AF.user_id + + + diff --git a/src/main/resources/mapper/CommentReadService.xml b/src/main/resources/mapper/CommentReadService.xml index acaf8dc..e8d623d 100644 --- a/src/main/resources/mapper/CommentReadService.xml +++ b/src/main/resources/mapper/CommentReadService.xml @@ -21,4 +21,22 @@ where C.article_id = #{articleId} + \ No newline at end of file diff --git a/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java b/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java index 588b9b4..d870d8e 100644 --- a/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java +++ b/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java @@ -6,6 +6,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import io.spring.application.ArticleQueryService; +import io.spring.application.CursorPageParameter; +import io.spring.application.CursorPager; +import io.spring.application.CursorPager.Direction; import io.spring.application.Page; import io.spring.application.data.ArticleData; import io.spring.application.data.ArticleDataList; @@ -16,6 +19,7 @@ import io.spring.core.favorite.ArticleFavoriteRepository; import io.spring.core.user.FollowRelation; import io.spring.core.user.User; import io.spring.core.user.UserRepository; +import io.spring.infrastructure.DbTestBase; import io.spring.infrastructure.repository.MyBatisArticleFavoriteRepository; import io.spring.infrastructure.repository.MyBatisArticleRepository; import io.spring.infrastructure.repository.MyBatisUserRepository; @@ -24,21 +28,16 @@ import java.util.Optional; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) -@MybatisTest @Import({ ArticleQueryService.class, MyBatisUserRepository.class, MyBatisArticleRepository.class, MyBatisArticleFavoriteRepository.class }) -public class ArticleQueryServiceTest { +public class ArticleQueryServiceTest extends DbTestBase { @Autowired private ArticleQueryService queryService; @Autowired private ArticleRepository articleRepository; @@ -111,6 +110,40 @@ public class ArticleQueryServiceTest { assertEquals(nodata.getArticleDatas().size(), 0); } + @Test + public void should_get_default_article_list_by_cursor() { + Article anotherArticle = + new Article( + "new article", + "desc", + "body", + Arrays.asList("test"), + user.getId(), + new DateTime().minusHours(1)); + articleRepository.save(anotherArticle); + + CursorPager recentArticles = + queryService.findRecentArticlesWithCursor( + null, null, null, new CursorPageParameter("", 20, Direction.NEXT), user); + assertEquals(recentArticles.getData().size(), 2); + assertEquals(recentArticles.getData().get(0).getId(), article.getId()); + + CursorPager nodata = + queryService.findRecentArticlesWithCursor( + null, + null, + null, + new CursorPageParameter(recentArticles.getEndCursor(), 20, Direction.NEXT), + user); + assertEquals(nodata.getData().size(), 0); + assertEquals(nodata.getStartCursor(), ""); + + CursorPager prevArticles = + queryService.findRecentArticlesWithCursor( + null, null, null, new CursorPageParameter("", 20, Direction.PREV), user); + assertEquals(prevArticles.getData().size(), 2); + } + @Test public void should_query_article_by_author() { User anotherUser = new User("other@email.com", "other", "123", "", ""); diff --git a/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java b/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java index b84be61..8cb202f 100644 --- a/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java +++ b/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java @@ -12,6 +12,7 @@ import io.spring.core.comment.CommentRepository; import io.spring.core.user.FollowRelation; import io.spring.core.user.User; import io.spring.core.user.UserRepository; +import io.spring.infrastructure.DbTestBase; import io.spring.infrastructure.repository.MyBatisArticleRepository; import io.spring.infrastructure.repository.MyBatisCommentRepository; import io.spring.infrastructure.repository.MyBatisUserRepository; @@ -20,21 +21,16 @@ import java.util.List; import java.util.Optional; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -@MybatisTest -@RunWith(SpringRunner.class) @Import({ MyBatisCommentRepository.class, MyBatisUserRepository.class, CommentQueryService.class, MyBatisArticleRepository.class }) -public class CommentQueryServiceTest { +public class CommentQueryServiceTest extends DbTestBase { @Autowired private CommentRepository commentRepository; @Autowired private UserRepository userRepository; diff --git a/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java b/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java index 3931e54..2b6ecae 100644 --- a/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java +++ b/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java @@ -1,37 +1,31 @@ package io.spring.application.profile; +import static org.junit.Assert.assertTrue; + import io.spring.application.ProfileQueryService; import io.spring.application.data.ProfileData; import io.spring.core.user.User; import io.spring.core.user.UserRepository; +import io.spring.infrastructure.DbTestBase; import io.spring.infrastructure.repository.MyBatisUserRepository; +import java.util.Optional; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -import java.util.Optional; - -import static org.junit.Assert.assertTrue; - -@RunWith(SpringRunner.class) -@MybatisTest @Import({ProfileQueryService.class, MyBatisUserRepository.class}) -public class ProfileQueryServiceTest { - @Autowired - private ProfileQueryService profileQueryService; - @Autowired - private UserRepository userRepository; +public class ProfileQueryServiceTest extends DbTestBase { + @Autowired private ProfileQueryService profileQueryService; + @Autowired private UserRepository userRepository; - @Test - public void should_fetch_profile_success() { - User currentUser = new User("a@test.com", "a", "123", "", ""); - User profileUser = new User("p@test.com", "p", "123", "", ""); - userRepository.save(profileUser); + @Test + public void should_fetch_profile_success() { + User currentUser = new User("a@test.com", "a", "123", "", ""); + User profileUser = new User("p@test.com", "p", "123", "", ""); + userRepository.save(profileUser); - Optional optional = profileQueryService.findByUsername(profileUser.getUsername(), currentUser); - assertTrue(optional.isPresent()); - } -} \ No newline at end of file + Optional optional = + profileQueryService.findByUsername(profileUser.getUsername(), currentUser); + assertTrue(optional.isPresent()); + } +} diff --git a/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java b/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java index b379313..b2cbbfc 100644 --- a/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java +++ b/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java @@ -5,19 +5,15 @@ import static org.junit.Assert.assertTrue; import io.spring.application.TagsQueryService; import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; +import io.spring.infrastructure.DbTestBase; import io.spring.infrastructure.repository.MyBatisArticleRepository; import java.util.Arrays; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) -@MybatisTest @Import({TagsQueryService.class, MyBatisArticleRepository.class}) -public class TagsQueryServiceTest { +public class TagsQueryServiceTest extends DbTestBase { @Autowired private TagsQueryService tagsQueryService; @Autowired private ArticleRepository articleRepository; diff --git a/src/test/java/io/spring/infrastructure/DbTestBase.java b/src/test/java/io/spring/infrastructure/DbTestBase.java new file mode 100644 index 0000000..a4d4a60 --- /dev/null +++ b/src/test/java/io/spring/infrastructure/DbTestBase.java @@ -0,0 +1,14 @@ +package io.spring.infrastructure; + +import org.junit.runner.RunWith; +import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +@ActiveProfiles("test") +@AutoConfigureTestDatabase(replace = Replace.NONE) +@MybatisTest +@RunWith(SpringRunner.class) +public abstract class DbTestBase {} diff --git a/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java b/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java index 4675773..0969d3a 100644 --- a/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java +++ b/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java @@ -13,11 +13,13 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@ActiveProfiles("test") @SpringBootTest -@AutoConfigureTestDatabase +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@RunWith(SpringRunner.class) public class ArticleRepositoryTransactionTest { @Autowired private ArticleRepository articleRepository; diff --git a/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java b/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java index 12040d6..a8a4551 100644 --- a/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java @@ -10,22 +10,18 @@ import io.spring.core.article.ArticleRepository; import io.spring.core.article.Tag; import io.spring.core.user.User; import io.spring.core.user.UserRepository; +import io.spring.infrastructure.DbTestBase; import io.spring.infrastructure.repository.MyBatisArticleRepository; import io.spring.infrastructure.repository.MyBatisUserRepository; import java.util.Arrays; import java.util.Optional; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -@MybatisTest -@RunWith(SpringRunner.class) @Import({MyBatisArticleRepository.class, MyBatisUserRepository.class}) -public class MyBatisArticleRepositoryTest { +public class MyBatisArticleRepositoryTest extends DbTestBase { @Autowired private ArticleRepository articleRepository; @Autowired private UserRepository userRepository; diff --git a/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java b/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java index 2ad0043..0198b23 100644 --- a/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java @@ -1,34 +1,28 @@ package io.spring.infrastructure.comment; -import io.spring.core.comment.Comment; -import io.spring.core.comment.CommentRepository; -import io.spring.infrastructure.repository.MyBatisCommentRepository; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.Optional; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -@MybatisTest -@RunWith(SpringRunner.class) +import io.spring.core.comment.Comment; +import io.spring.core.comment.CommentRepository; +import io.spring.infrastructure.DbTestBase; +import io.spring.infrastructure.repository.MyBatisCommentRepository; +import java.util.Optional; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; + @Import({MyBatisCommentRepository.class}) -public class MyBatisCommentRepositoryTest { - @Autowired - private CommentRepository commentRepository; +public class MyBatisCommentRepositoryTest extends DbTestBase { + @Autowired private CommentRepository commentRepository; - @Test - public void should_create_and_fetch_comment_success() { - Comment comment = new Comment("content", "123", "456"); - commentRepository.save(comment); + @Test + public void should_create_and_fetch_comment_success() { + Comment comment = new Comment("content", "123", "456"); + commentRepository.save(comment); - Optional optional = commentRepository.findById("456", comment.getId()); - assertTrue(optional.isPresent()); - assertEquals(optional.get(), comment); - } -} \ No newline at end of file + Optional optional = commentRepository.findById("456", comment.getId()); + assertTrue(optional.isPresent()); + assertEquals(optional.get(), comment); + } +} diff --git a/src/test/java/io/spring/infrastructure/favorite/MyBatisArticleFavoriteRepositoryTest.java b/src/test/java/io/spring/infrastructure/favorite/MyBatisArticleFavoriteRepositoryTest.java index d874831..3c5361d 100644 --- a/src/test/java/io/spring/infrastructure/favorite/MyBatisArticleFavoriteRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/favorite/MyBatisArticleFavoriteRepositoryTest.java @@ -1,40 +1,36 @@ package io.spring.infrastructure.favorite; -import io.spring.core.favorite.ArticleFavorite; -import io.spring.core.favorite.ArticleFavoriteRepository; -import io.spring.infrastructure.repository.MyBatisArticleFavoriteRepository; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; - import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -@RunWith(SpringRunner.class) -@MybatisTest +import io.spring.core.favorite.ArticleFavorite; +import io.spring.core.favorite.ArticleFavoriteRepository; +import io.spring.infrastructure.DbTestBase; +import io.spring.infrastructure.repository.MyBatisArticleFavoriteRepository; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; + @Import({MyBatisArticleFavoriteRepository.class}) -public class MyBatisArticleFavoriteRepositoryTest { - @Autowired - private ArticleFavoriteRepository articleFavoriteRepository; +public class MyBatisArticleFavoriteRepositoryTest extends DbTestBase { + @Autowired private ArticleFavoriteRepository articleFavoriteRepository; - @Autowired - private io.spring.infrastructure.mybatis.mapper.ArticleFavoriteMapper articleFavoriteMapper; + @Autowired + private io.spring.infrastructure.mybatis.mapper.ArticleFavoriteMapper articleFavoriteMapper; - @Test - public void should_save_and_fetch_articleFavorite_success() { - ArticleFavorite articleFavorite = new ArticleFavorite("123", "456"); - articleFavoriteRepository.save(articleFavorite); - assertNotNull(articleFavoriteMapper.find(articleFavorite.getArticleId(), articleFavorite.getUserId())); - } + @Test + public void should_save_and_fetch_articleFavorite_success() { + ArticleFavorite articleFavorite = new ArticleFavorite("123", "456"); + articleFavoriteRepository.save(articleFavorite); + assertNotNull( + articleFavoriteMapper.find(articleFavorite.getArticleId(), articleFavorite.getUserId())); + } - @Test - public void should_remove_favorite_success() { - ArticleFavorite articleFavorite = new ArticleFavorite("123", "456"); - articleFavoriteRepository.save(articleFavorite); - articleFavoriteRepository.remove(articleFavorite); - assertFalse(articleFavoriteRepository.find("123", "456").isPresent()); - } -} \ No newline at end of file + @Test + public void should_remove_favorite_success() { + ArticleFavorite articleFavorite = new ArticleFavorite("123", "456"); + articleFavoriteRepository.save(articleFavorite); + articleFavoriteRepository.remove(articleFavorite); + assertFalse(articleFavoriteRepository.find("123", "456").isPresent()); + } +} diff --git a/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java b/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java index a218187..c873d78 100644 --- a/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java @@ -1,82 +1,76 @@ package io.spring.infrastructure.user; -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.repository.MyBatisUserRepository; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.Optional; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -@RunWith(SpringRunner.class) -@MybatisTest +import io.spring.core.user.FollowRelation; +import io.spring.core.user.User; +import io.spring.core.user.UserRepository; +import io.spring.infrastructure.DbTestBase; +import io.spring.infrastructure.repository.MyBatisUserRepository; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; + @Import(MyBatisUserRepository.class) -public class MyBatisUserRepositoryTest { - @Autowired - private UserRepository userRepository; - private User user; +public class MyBatisUserRepositoryTest extends DbTestBase { + @Autowired private UserRepository userRepository; + private User user; - @Before - public void setUp() { - user = new User("aisensiy@163.com", "aisensiy", "123", "", "default"); - } + @Before + public void setUp() { + user = new User("aisensiy@163.com", "aisensiy", "123", "", "default"); + } - @Test - public void should_save_and_fetch_user_success() { - userRepository.save(user); - Optional userOptional = userRepository.findByUsername("aisensiy"); - assertEquals(userOptional.get(), user); - Optional userOptional2 = userRepository.findByEmail("aisensiy@163.com"); - assertEquals(userOptional2.get(), user); - } + @Test + public void should_save_and_fetch_user_success() { + userRepository.save(user); + Optional userOptional = userRepository.findByUsername("aisensiy"); + assertEquals(userOptional.get(), user); + Optional userOptional2 = userRepository.findByEmail("aisensiy@163.com"); + assertEquals(userOptional2.get(), user); + } - @Test - public void should_update_user_success() { - String newEmail = "newemail@email.com"; - user.update(newEmail, "", "", "", ""); - userRepository.save(user); - Optional optional = userRepository.findByUsername(user.getUsername()); - assertTrue(optional.isPresent()); - assertEquals(optional.get().getEmail(), newEmail); + @Test + public void should_update_user_success() { + String newEmail = "newemail@email.com"; + user.update(newEmail, "", "", "", ""); + userRepository.save(user); + Optional optional = userRepository.findByUsername(user.getUsername()); + assertTrue(optional.isPresent()); + assertEquals(optional.get().getEmail(), newEmail); - String newUsername = "newUsername"; - user.update("", newUsername, "", "", ""); - userRepository.save(user); - optional = userRepository.findByEmail(user.getEmail()); - assertTrue(optional.isPresent()); - assertEquals(optional.get().getUsername(), newUsername); - assertEquals(optional.get().getImage(), user.getImage()); - } + String newUsername = "newUsername"; + user.update("", newUsername, "", "", ""); + userRepository.save(user); + optional = userRepository.findByEmail(user.getEmail()); + assertTrue(optional.isPresent()); + assertEquals(optional.get().getUsername(), newUsername); + assertEquals(optional.get().getImage(), user.getImage()); + } - @Test - public void should_create_new_user_follow_success() { - User other = new User("other@example.com", "other", "123", "", ""); - userRepository.save(other); + @Test + public void should_create_new_user_follow_success() { + User other = new User("other@example.com", "other", "123", "", ""); + userRepository.save(other); - FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); - userRepository.saveRelation(followRelation); - assertTrue(userRepository.findRelation(user.getId(), other.getId()).isPresent()); - } + FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); + userRepository.saveRelation(followRelation); + assertTrue(userRepository.findRelation(user.getId(), other.getId()).isPresent()); + } - @Test - public void should_unfollow_user_success() { - User other = new User("other@example.com", "other", "123", "", ""); - userRepository.save(other); + @Test + public void should_unfollow_user_success() { + User other = new User("other@example.com", "other", "123", "", ""); + userRepository.save(other); - FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); - userRepository.saveRelation(followRelation); + FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); + userRepository.saveRelation(followRelation); - userRepository.removeRelation(followRelation); - assertFalse(userRepository.findRelation(user.getId(), other.getId()).isPresent()); - } -} \ No newline at end of file + userRepository.removeRelation(followRelation); + assertFalse(userRepository.findRelation(user.getId(), other.getId()).isPresent()); + } +}