refactor: application level prepare for graphql and switch to sqlite
This commit is contained in:
parent
36e33e7730
commit
01fac42c64
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
.gradle
|
.gradle
|
||||||
/build/
|
/build/
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
*.db
|
||||||
|
|
||||||
### STS ###
|
### STS ###
|
||||||
.apt_generated
|
.apt_generated
|
||||||
|
11
build.gradle
11
build.gradle
@ -4,6 +4,7 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
|
id "com.netflix.dgs.codegen" version "4.2.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
version = '0.0.1-SNAPSHOT'
|
version = '0.0.1-SNAPSHOT'
|
||||||
@ -13,6 +14,7 @@ targetCompatibility = JavaVersion.VERSION_1_8
|
|||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
@ -26,11 +28,11 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
|
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3'
|
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 'org.flywaydb:flyway-core'
|
||||||
implementation 'io.jsonwebtoken:jjwt:0.9.1'
|
implementation 'io.jsonwebtoken:jjwt:0.9.1'
|
||||||
implementation 'joda-time:joda-time:2.10.6'
|
implementation 'joda-time:joda-time:2.10.6'
|
||||||
|
implementation 'org.xerial:sqlite-jdbc:3.34.0'
|
||||||
runtimeOnly 'com.h2database:h2'
|
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
@ -47,3 +49,8 @@ dependencies {
|
|||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
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
|
||||||
|
}
|
@ -2,8 +2,9 @@ package io.spring;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class)
|
||||||
public class RealworldApplication {
|
public class RealworldApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -34,25 +34,39 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
|
|
||||||
if (h2ConsoleEnabled) {
|
if (h2ConsoleEnabled) {
|
||||||
http.authorizeRequests()
|
http.authorizeRequests()
|
||||||
.antMatchers("/h2-console", "/h2-console/**").permitAll()
|
.antMatchers("/h2-console", "/h2-console/**")
|
||||||
|
.permitAll()
|
||||||
.and()
|
.and()
|
||||||
.headers().frameOptions().sameOrigin();
|
.headers()
|
||||||
|
.frameOptions()
|
||||||
|
.sameOrigin();
|
||||||
}
|
}
|
||||||
|
|
||||||
http.csrf().disable()
|
http.csrf()
|
||||||
|
.disable()
|
||||||
.cors()
|
.cors()
|
||||||
.and()
|
.and()
|
||||||
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
|
.exceptionHandling()
|
||||||
|
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
|
||||||
|
.and()
|
||||||
|
.sessionManagement()
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
.and()
|
.and()
|
||||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers(HttpMethod.OPTIONS).permitAll()
|
.antMatchers(HttpMethod.OPTIONS)
|
||||||
.antMatchers("/graphiql").permitAll()
|
.permitAll()
|
||||||
.antMatchers("/graphql").permitAll()
|
.antMatchers("/graphiql")
|
||||||
.antMatchers(HttpMethod.GET, "/articles/feed").authenticated()
|
.permitAll()
|
||||||
.antMatchers(HttpMethod.POST, "/users", "/users/login").permitAll()
|
.antMatchers("/graphql")
|
||||||
.antMatchers(HttpMethod.GET, "/articles/**", "/profiles/**", "/tags").permitAll()
|
.permitAll()
|
||||||
.anyRequest().authenticated();
|
.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);
|
http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
}
|
}
|
||||||
@ -61,10 +75,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
public CorsConfigurationSource corsConfigurationSource() {
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
final CorsConfiguration configuration = new CorsConfiguration();
|
final CorsConfiguration configuration = new CorsConfiguration();
|
||||||
configuration.setAllowedOrigins(asList("*"));
|
configuration.setAllowedOrigins(asList("*"));
|
||||||
configuration.setAllowedMethods(asList("HEAD",
|
configuration.setAllowedMethods(asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
|
||||||
"GET", "POST", "PUT", "DELETE", "PATCH"));
|
|
||||||
// setAllowCredentials(true) is important, otherwise:
|
// 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'.
|
// 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);
|
configuration.setAllowCredentials(false);
|
||||||
// setAllowedHeaders is important! Without it, OPTIONS preflight request
|
// setAllowedHeaders is important! Without it, OPTIONS preflight request
|
||||||
// will fail with 403 Invalid CORS request
|
// will fail with 403 Invalid CORS request
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package io.spring.application;
|
package io.spring.application;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import io.spring.application.data.ArticleData;
|
import io.spring.application.data.ArticleData;
|
||||||
import io.spring.application.data.ArticleDataList;
|
import io.spring.application.data.ArticleDataList;
|
||||||
import io.spring.application.data.ArticleFavoriteCount;
|
import io.spring.application.data.ArticleFavoriteCount;
|
||||||
@ -7,17 +9,15 @@ import io.spring.core.user.User;
|
|||||||
import io.spring.infrastructure.mybatis.readservice.ArticleFavoritesReadService;
|
import io.spring.infrastructure.mybatis.readservice.ArticleFavoritesReadService;
|
||||||
import io.spring.infrastructure.mybatis.readservice.ArticleReadService;
|
import io.spring.infrastructure.mybatis.readservice.ArticleReadService;
|
||||||
import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService;
|
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.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import static java.util.stream.Collectors.toList;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ArticleQueryService {
|
public class ArticleQueryService {
|
||||||
@ -26,7 +26,8 @@ public class ArticleQueryService {
|
|||||||
private ArticleFavoritesReadService articleFavoritesReadService;
|
private ArticleFavoritesReadService articleFavoritesReadService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ArticleQueryService(ArticleReadService articleReadService,
|
public ArticleQueryService(
|
||||||
|
ArticleReadService articleReadService,
|
||||||
UserRelationshipQueryService userRelationshipQueryService,
|
UserRelationshipQueryService userRelationshipQueryService,
|
||||||
ArticleFavoritesReadService articleFavoritesReadService) {
|
ArticleFavoritesReadService articleFavoritesReadService) {
|
||||||
this.articleReadService = articleReadService;
|
this.articleReadService = articleReadService;
|
||||||
@ -58,7 +59,49 @@ public class ArticleQueryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArticleDataList findRecentArticles(String tag, String author, String favoritedBy, Page page, User currentUser) {
|
public CursorPager<ArticleData> findRecentArticlesWithCursor(
|
||||||
|
String tag, String author, String favoritedBy, CursorPageParameter page, User currentUser) {
|
||||||
|
List<String> 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<ArticleData> articles = articleReadService.findArticles(articleIds);
|
||||||
|
fillExtraInfo(articles, currentUser);
|
||||||
|
|
||||||
|
return new CursorPager<>(articles, page.getDirection(), hasExtra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CursorPager<ArticleData> findUserFeedWithCursor(User user, CursorPageParameter page) {
|
||||||
|
List<String> followdUsers = userRelationshipQueryService.followedUsers(user.getId());
|
||||||
|
if (followdUsers.size() == 0) {
|
||||||
|
return new CursorPager<>(new ArrayList<>(), page.getDirection(), false);
|
||||||
|
} else {
|
||||||
|
List<ArticleData> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleDataList findRecentArticles(
|
||||||
|
String tag, String author, String favoritedBy, Page page, User currentUser) {
|
||||||
List<String> articleIds = articleReadService.queryArticles(tag, author, favoritedBy, page);
|
List<String> articleIds = articleReadService.queryArticles(tag, author, favoritedBy, page);
|
||||||
int articleCount = articleReadService.countArticle(tag, author, favoritedBy);
|
int articleCount = articleReadService.countArticle(tag, author, favoritedBy);
|
||||||
if (articleIds.size() == 0) {
|
if (articleIds.size() == 0) {
|
||||||
@ -70,53 +113,6 @@ public class ArticleQueryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillExtraInfo(List<ArticleData> articles, User currentUser) {
|
|
||||||
setFavoriteCount(articles);
|
|
||||||
if (currentUser != null) {
|
|
||||||
setIsFavorite(articles, currentUser);
|
|
||||||
setIsFollowingAuthor(articles, currentUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setIsFollowingAuthor(List<ArticleData> articles, User currentUser) {
|
|
||||||
Set<String> 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFavoriteCount(List<ArticleData> articles) {
|
|
||||||
List<ArticleFavoriteCount> favoritesCounts = articleFavoritesReadService.articlesFavoriteCount(articles.stream().map(ArticleData::getId).collect(toList()));
|
|
||||||
Map<String, Integer> countMap = new HashMap<>();
|
|
||||||
favoritesCounts.forEach(item -> {
|
|
||||||
countMap.put(item.getId(), item.getCount());
|
|
||||||
});
|
|
||||||
articles.forEach(articleData -> articleData.setFavoritesCount(countMap.get(articleData.getId())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setIsFavorite(List<ArticleData> articles, User currentUser) {
|
|
||||||
Set<String> favoritedArticles = articleFavoritesReadService.userFavorites(articles.stream().map(articleData -> articleData.getId()).collect(toList()), currentUser);
|
|
||||||
|
|
||||||
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(
|
|
||||||
userRelationshipQueryService.isUserFollowing(
|
|
||||||
user.getId(),
|
|
||||||
articleData.getProfileData().getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArticleDataList findUserFeed(User user, Page page) {
|
public ArticleDataList findUserFeed(User user, Page page) {
|
||||||
List<String> followdUsers = userRelationshipQueryService.followedUsers(user.getId());
|
List<String> followdUsers = userRelationshipQueryService.followedUsers(user.getId());
|
||||||
if (followdUsers.size() == 0) {
|
if (followdUsers.size() == 0) {
|
||||||
@ -128,5 +124,64 @@ public class ArticleQueryService {
|
|||||||
return new ArticleDataList(articles, count);
|
return new ArticleDataList(articles, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillExtraInfo(List<ArticleData> articles, User currentUser) {
|
||||||
|
setFavoriteCount(articles);
|
||||||
|
if (currentUser != null) {
|
||||||
|
setIsFavorite(articles, currentUser);
|
||||||
|
setIsFollowingAuthor(articles, currentUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setIsFollowingAuthor(List<ArticleData> articles, User currentUser) {
|
||||||
|
Set<String> 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFavoriteCount(List<ArticleData> articles) {
|
||||||
|
List<ArticleFavoriteCount> favoritesCounts =
|
||||||
|
articleFavoritesReadService.articlesFavoriteCount(
|
||||||
|
articles.stream().map(ArticleData::getId).collect(toList()));
|
||||||
|
Map<String, Integer> countMap = new HashMap<>();
|
||||||
|
favoritesCounts.forEach(
|
||||||
|
item -> {
|
||||||
|
countMap.put(item.getId(), item.getCount());
|
||||||
|
});
|
||||||
|
articles.forEach(
|
||||||
|
articleData -> articleData.setFavoritesCount(countMap.get(articleData.getId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setIsFavorite(List<ArticleData> articles, User currentUser) {
|
||||||
|
Set<String> favoritedArticles =
|
||||||
|
articleFavoritesReadService.userFavorites(
|
||||||
|
articles.stream().map(articleData -> articleData.getId()).collect(toList()),
|
||||||
|
currentUser);
|
||||||
|
|
||||||
|
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(
|
||||||
|
userRelationshipQueryService.isUserFollowing(
|
||||||
|
user.getId(), articleData.getProfileData().getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
package io.spring.application;
|
package io.spring.application;
|
||||||
|
|
||||||
import io.spring.application.data.CommentData;
|
import io.spring.application.data.CommentData;
|
||||||
import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService;
|
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.infrastructure.mybatis.readservice.CommentReadService;
|
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.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CommentQueryService {
|
public class CommentQueryService {
|
||||||
private CommentReadService commentReadService;
|
private CommentReadService commentReadService;
|
||||||
private UserRelationshipQueryService userRelationshipQueryService;
|
private UserRelationshipQueryService userRelationshipQueryService;
|
||||||
|
|
||||||
public CommentQueryService(CommentReadService commentReadService, UserRelationshipQueryService userRelationshipQueryService) {
|
public CommentQueryService(
|
||||||
|
CommentReadService commentReadService,
|
||||||
|
UserRelationshipQueryService userRelationshipQueryService) {
|
||||||
this.commentReadService = commentReadService;
|
this.commentReadService = commentReadService;
|
||||||
this.userRelationshipQueryService = userRelationshipQueryService;
|
this.userRelationshipQueryService = userRelationshipQueryService;
|
||||||
}
|
}
|
||||||
@ -26,10 +29,11 @@ public class CommentQueryService {
|
|||||||
if (commentData == null) {
|
if (commentData == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} else {
|
} else {
|
||||||
commentData.getProfileData().setFollowing(
|
commentData
|
||||||
|
.getProfileData()
|
||||||
|
.setFollowing(
|
||||||
userRelationshipQueryService.isUserFollowing(
|
userRelationshipQueryService.isUserFollowing(
|
||||||
user.getId(),
|
user.getId(), commentData.getProfileData().getId()));
|
||||||
commentData.getProfileData().getId()));
|
|
||||||
}
|
}
|
||||||
return Optional.ofNullable(commentData);
|
return Optional.ofNullable(commentData);
|
||||||
}
|
}
|
||||||
@ -37,8 +41,14 @@ public class CommentQueryService {
|
|||||||
public List<CommentData> findByArticleId(String articleId, User user) {
|
public List<CommentData> findByArticleId(String articleId, User user) {
|
||||||
List<CommentData> comments = commentReadService.findByArticleId(articleId);
|
List<CommentData> comments = commentReadService.findByArticleId(articleId);
|
||||||
if (comments.size() > 0 && user != null) {
|
if (comments.size() > 0 && user != null) {
|
||||||
Set<String> followingAuthors = userRelationshipQueryService.followingAuthors(user.getId(), comments.stream().map(commentData -> commentData.getProfileData().getId()).collect(Collectors.toList()));
|
Set<String> followingAuthors =
|
||||||
comments.forEach(commentData -> {
|
userRelationshipQueryService.followingAuthors(
|
||||||
|
user.getId(),
|
||||||
|
comments.stream()
|
||||||
|
.map(commentData -> commentData.getProfileData().getId())
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
comments.forEach(
|
||||||
|
commentData -> {
|
||||||
if (followingAuthors.contains(commentData.getProfileData().getId())) {
|
if (followingAuthors.contains(commentData.getProfileData().getId())) {
|
||||||
commentData.getProfileData().setFollowing(true);
|
commentData.getProfileData().setFollowing(true);
|
||||||
}
|
}
|
||||||
@ -46,4 +56,34 @@ public class CommentQueryService {
|
|||||||
}
|
}
|
||||||
return comments;
|
return comments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CursorPager<CommentData> findByArticleIdWithCursor(
|
||||||
|
String articleId, User user, CursorPageParameter page) {
|
||||||
|
List<CommentData> comments = commentReadService.findByArticleIdWithCursor(articleId, page);
|
||||||
|
if (comments.isEmpty()) {
|
||||||
|
return new CursorPager<>(new ArrayList<>(), page.getDirection(), false);
|
||||||
|
}
|
||||||
|
if (user != null) {
|
||||||
|
Set<String> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
44
src/main/java/io/spring/application/CursorPageParameter.java
Normal file
44
src/main/java/io/spring/application/CursorPageParameter.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/main/java/io/spring/application/CursorPager.java
Normal file
44
src/main/java/io/spring/application/CursorPager.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package io.spring.application;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class CursorPager<T extends Node> {
|
||||||
|
private List<T> data;
|
||||||
|
private boolean next;
|
||||||
|
private boolean previous;
|
||||||
|
|
||||||
|
public CursorPager(List<T> 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
|
||||||
|
}
|
||||||
|
}
|
5
src/main/java/io/spring/application/Node.java
Normal file
5
src/main/java/io/spring/application/Node.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package io.spring.application;
|
||||||
|
|
||||||
|
public interface Node {
|
||||||
|
String getCursor();
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package io.spring.application;
|
package io.spring.application;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Data
|
@Data
|
||||||
@Getter
|
|
||||||
public class Page {
|
public class Page {
|
||||||
private static final int MAX_LIMIT = 100;
|
private static final int MAX_LIMIT = 100;
|
||||||
private int offset = 0;
|
private int offset = 0;
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package io.spring.application.data;
|
package io.spring.application.data;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import java.util.List;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ArticleData {
|
public class ArticleData implements io.spring.application.Node {
|
||||||
private String id;
|
private String id;
|
||||||
private String slug;
|
private String slug;
|
||||||
private String title;
|
private String title;
|
||||||
@ -22,7 +21,12 @@ public class ArticleData {
|
|||||||
private DateTime createdAt;
|
private DateTime createdAt;
|
||||||
private DateTime updatedAt;
|
private DateTime updatedAt;
|
||||||
private List<String> tagList;
|
private List<String> tagList;
|
||||||
|
|
||||||
@JsonProperty("author")
|
@JsonProperty("author")
|
||||||
private ProfileData profileData;
|
private ProfileData profileData;
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCursor() {
|
||||||
|
return String.valueOf(getUpdatedAt().getMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package io.spring.application.data;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.spring.application.Node;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@ -10,13 +11,18 @@ import org.joda.time.DateTime;
|
|||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class CommentData {
|
public class CommentData implements Node {
|
||||||
private String id;
|
private String id;
|
||||||
private String body;
|
private String body;
|
||||||
@JsonIgnore
|
@JsonIgnore private String articleId;
|
||||||
private String articleId;
|
|
||||||
private DateTime createdAt;
|
private DateTime createdAt;
|
||||||
private DateTime updatedAt;
|
private DateTime updatedAt;
|
||||||
|
|
||||||
@JsonProperty("author")
|
@JsonProperty("author")
|
||||||
private ProfileData profileData;
|
private ProfileData profileData;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCursor() {
|
||||||
|
return String.valueOf(createdAt.getMillis());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,42 @@
|
|||||||
package io.spring.infrastructure.mybatis.readservice;
|
package io.spring.infrastructure.mybatis.readservice;
|
||||||
|
|
||||||
|
import io.spring.application.CursorPageParameter;
|
||||||
import io.spring.application.Page;
|
import io.spring.application.Page;
|
||||||
import io.spring.application.data.ArticleData;
|
import io.spring.application.data.ArticleData;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface ArticleReadService {
|
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<String> queryArticles(@Param("tag") String tag, @Param("author") String author, @Param("favoritedBy") String favoritedBy, @Param("page") Page page);
|
List<String> 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<ArticleData> findArticles(@Param("articleIds") List<String> articleIds);
|
List<ArticleData> findArticles(@Param("articleIds") List<String> articleIds);
|
||||||
|
|
||||||
List<ArticleData> findArticlesOfAuthors(@Param("authors") List<String> authors, @Param("page") Page page);
|
List<ArticleData> findArticlesOfAuthors(
|
||||||
|
@Param("authors") List<String> authors, @Param("page") Page page);
|
||||||
|
|
||||||
|
List<ArticleData> findArticlesOfAuthorsWithCursor(
|
||||||
|
@Param("authors") List<String> authors, @Param("page") CursorPageParameter page);
|
||||||
|
|
||||||
int countFeedSize(@Param("authors") List<String> authors);
|
int countFeedSize(@Param("authors") List<String> authors);
|
||||||
|
|
||||||
|
List<String> findArticlesWithCursor(
|
||||||
|
@Param("tag") String tag,
|
||||||
|
@Param("author") String author,
|
||||||
|
@Param("favoritedBy") String favoritedBy,
|
||||||
|
@Param("page") CursorPageParameter page);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
package io.spring.infrastructure.mybatis.readservice;
|
package io.spring.infrastructure.mybatis.readservice;
|
||||||
|
|
||||||
|
import io.spring.application.CursorPageParameter;
|
||||||
import io.spring.application.data.CommentData;
|
import io.spring.application.data.CommentData;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface CommentReadService {
|
public interface CommentReadService {
|
||||||
CommentData findById(@Param("id") String id);
|
CommentData findById(@Param("id") String id);
|
||||||
|
|
||||||
List<CommentData> findByArticleId(@Param("articleId") String articleId);
|
List<CommentData> findByArticleId(@Param("articleId") String articleId);
|
||||||
|
|
||||||
|
List<CommentData> findByArticleIdWithCursor(
|
||||||
|
@Param("articleId") String articleId, @Param("page") CursorPageParameter page);
|
||||||
}
|
}
|
||||||
|
1
src/main/resources/application-test.properties
Normal file
1
src/main/resources/application-test.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
spring.datasource.url=jdbc:sqlite::memory:
|
@ -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
|
spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true
|
||||||
image.default=https://static.productionready.io/images/smiley-cyrus.jpg
|
image.default=https://static.productionready.io/images/smiley-cyrus.jpg
|
||||||
jwt.secret=nRvyYC4soFxBdZ-F-5Nnzz5USXstR1YylsTd-mA0aKtI9HUlriGrtkf-TiuDapkLiUCogO3JOK7kwZisrHp6wA
|
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.type-handlers-package=io.spring.infrastructure.mybatis
|
||||||
mybatis.mapper-locations=mapper/*.xml
|
mybatis.mapper-locations=mapper/*.xml
|
||||||
logging.level.io.spring.infrastructure.mybatis.readservice.ArticleReadService=DEBUG
|
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
|
# Uncomment the following line to enable and allow access to the h2-console
|
||||||
#spring.h2.console.enabled=true
|
#spring.h2.console.enabled=true
|
||||||
|
@ -24,6 +24,17 @@
|
|||||||
left join tags T on T.id = AT.tag_id
|
left join tags T on T.id = AT.tag_id
|
||||||
left join users U on U.id = A.user_id
|
left join users U on U.id = A.user_id
|
||||||
</sql>
|
</sql>
|
||||||
|
<sql id="selectArticleIds">
|
||||||
|
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
|
||||||
|
</sql>
|
||||||
|
|
||||||
<select id="findById" resultMap="transfer.data.articleData">
|
<select id="findById" resultMap="transfer.data.articleData">
|
||||||
<include refid="selectArticleData"/>
|
<include refid="selectArticleData"/>
|
||||||
@ -34,15 +45,7 @@
|
|||||||
where A.slug = #{slug}
|
where A.slug = #{slug}
|
||||||
</select>
|
</select>
|
||||||
<select id="queryArticles" resultMap="articleId">
|
<select id="queryArticles" resultMap="articleId">
|
||||||
select
|
<include refid="selectArticleIds" />
|
||||||
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
|
|
||||||
<where>
|
<where>
|
||||||
<if test="tag != null">
|
<if test="tag != null">
|
||||||
T.name = #{tag}
|
T.name = #{tag}
|
||||||
@ -101,6 +104,55 @@
|
|||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="findArticlesWithCursor" resultType="java.lang.String">
|
||||||
|
<include refid="selectArticleIds" />
|
||||||
|
<where>
|
||||||
|
<if test="tag != null">
|
||||||
|
T.name = #{tag}
|
||||||
|
</if>
|
||||||
|
<if test="author != null">
|
||||||
|
AND AU.username = #{author}
|
||||||
|
</if>
|
||||||
|
<if test="favoritedBy != null">
|
||||||
|
AND AFU.username = #{favoritedBy}
|
||||||
|
</if>
|
||||||
|
<if test='page.cursor != "" and page.direction.name() == "NEXT"'>
|
||||||
|
AND A.created_at < #{page.cursor}
|
||||||
|
</if>
|
||||||
|
<if test='page.cursor != "" and page.direction.name() == "PREV"'>
|
||||||
|
AND A.created_at > #{page.cursor}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
<if test='page.direction.name() == "NEXT"'>
|
||||||
|
order by A.created_at desc
|
||||||
|
</if>
|
||||||
|
<if test='page.direction.name() == "PREV"'>
|
||||||
|
order by A.created_at asc
|
||||||
|
</if>
|
||||||
|
limit #{page.queryLimit}
|
||||||
|
</select>
|
||||||
|
<select id="findArticlesOfAuthorsWithCursor" resultMap="transfer.data.articleData">
|
||||||
|
<include refid="selectArticleData"/>
|
||||||
|
<where>
|
||||||
|
A.user_id in
|
||||||
|
<foreach index="index" collection="authors" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
<if test='page.cursor != "" and page.direction.name() == "NEXT"'>
|
||||||
|
AND A.created_at < #{page.cursor}
|
||||||
|
</if>
|
||||||
|
<if test='page.cursor != "" and page.direction.name() == "PREV"'>
|
||||||
|
AND A.created_at > #{page.cursor}
|
||||||
|
</if>
|
||||||
|
<if test='page.direction.name() == "NEXT"'>
|
||||||
|
order by A.created_at desc
|
||||||
|
</if>
|
||||||
|
<if test='page.direction.name() == "PREV"'>
|
||||||
|
order by A.created_at asc
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
limit #{page.queryLimit}
|
||||||
|
</select>
|
||||||
|
|
||||||
<resultMap id="articleId" type="string">
|
<resultMap id="articleId" type="string">
|
||||||
<id javaType="string" column="articleId"/>
|
<id javaType="string" column="articleId"/>
|
||||||
|
@ -21,4 +21,22 @@
|
|||||||
<include refid="selectCommentData"/>
|
<include refid="selectCommentData"/>
|
||||||
where C.article_id = #{articleId}
|
where C.article_id = #{articleId}
|
||||||
</select>
|
</select>
|
||||||
|
<select id="findByArticleIdWithCursor" resultMap="transfer.data.commentData">
|
||||||
|
<include refid="selectCommentData"/>
|
||||||
|
<where>
|
||||||
|
C.article_id = #{articleId}
|
||||||
|
<if test='page.cursor != "" and page.direction.name() == "NEXT"'>
|
||||||
|
AND C.created_at < #{page.cursor}
|
||||||
|
</if>
|
||||||
|
<if test='page.cursor != "" and page.direction.name() == "PREV"'>
|
||||||
|
AND C.created_at > #{page.cursor}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
<if test='page.direction.name() == "NEXT"'>
|
||||||
|
order by C.created_at desc
|
||||||
|
</if>
|
||||||
|
<if test='page.direction.name() == "PREV"'>
|
||||||
|
order by C.created_at asc
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
@ -6,6 +6,9 @@ import static org.junit.Assert.assertNotNull;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import io.spring.application.ArticleQueryService;
|
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.Page;
|
||||||
import io.spring.application.data.ArticleData;
|
import io.spring.application.data.ArticleData;
|
||||||
import io.spring.application.data.ArticleDataList;
|
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.FollowRelation;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
|
import io.spring.infrastructure.DbTestBase;
|
||||||
import io.spring.infrastructure.repository.MyBatisArticleFavoriteRepository;
|
import io.spring.infrastructure.repository.MyBatisArticleFavoriteRepository;
|
||||||
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
||||||
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
||||||
@ -24,21 +28,16 @@ import java.util.Optional;
|
|||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@MybatisTest
|
|
||||||
@Import({
|
@Import({
|
||||||
ArticleQueryService.class,
|
ArticleQueryService.class,
|
||||||
MyBatisUserRepository.class,
|
MyBatisUserRepository.class,
|
||||||
MyBatisArticleRepository.class,
|
MyBatisArticleRepository.class,
|
||||||
MyBatisArticleFavoriteRepository.class
|
MyBatisArticleFavoriteRepository.class
|
||||||
})
|
})
|
||||||
public class ArticleQueryServiceTest {
|
public class ArticleQueryServiceTest extends DbTestBase {
|
||||||
@Autowired private ArticleQueryService queryService;
|
@Autowired private ArticleQueryService queryService;
|
||||||
|
|
||||||
@Autowired private ArticleRepository articleRepository;
|
@Autowired private ArticleRepository articleRepository;
|
||||||
@ -111,6 +110,40 @@ public class ArticleQueryServiceTest {
|
|||||||
assertEquals(nodata.getArticleDatas().size(), 0);
|
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<ArticleData> 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<ArticleData> nodata =
|
||||||
|
queryService.findRecentArticlesWithCursor(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
new CursorPageParameter(recentArticles.getEndCursor(), 20, Direction.NEXT),
|
||||||
|
user);
|
||||||
|
assertEquals(nodata.getData().size(), 0);
|
||||||
|
assertEquals(nodata.getStartCursor(), "");
|
||||||
|
|
||||||
|
CursorPager<ArticleData> prevArticles =
|
||||||
|
queryService.findRecentArticlesWithCursor(
|
||||||
|
null, null, null, new CursorPageParameter("", 20, Direction.PREV), user);
|
||||||
|
assertEquals(prevArticles.getData().size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_query_article_by_author() {
|
public void should_query_article_by_author() {
|
||||||
User anotherUser = new User("other@email.com", "other", "123", "", "");
|
User anotherUser = new User("other@email.com", "other", "123", "", "");
|
||||||
|
@ -12,6 +12,7 @@ import io.spring.core.comment.CommentRepository;
|
|||||||
import io.spring.core.user.FollowRelation;
|
import io.spring.core.user.FollowRelation;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
|
import io.spring.infrastructure.DbTestBase;
|
||||||
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
||||||
import io.spring.infrastructure.repository.MyBatisCommentRepository;
|
import io.spring.infrastructure.repository.MyBatisCommentRepository;
|
||||||
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
||||||
@ -20,21 +21,16 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
@MybatisTest
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@Import({
|
@Import({
|
||||||
MyBatisCommentRepository.class,
|
MyBatisCommentRepository.class,
|
||||||
MyBatisUserRepository.class,
|
MyBatisUserRepository.class,
|
||||||
CommentQueryService.class,
|
CommentQueryService.class,
|
||||||
MyBatisArticleRepository.class
|
MyBatisArticleRepository.class
|
||||||
})
|
})
|
||||||
public class CommentQueryServiceTest {
|
public class CommentQueryServiceTest extends DbTestBase {
|
||||||
@Autowired private CommentRepository commentRepository;
|
@Autowired private CommentRepository commentRepository;
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
|
@ -1,29 +1,22 @@
|
|||||||
package io.spring.application.profile;
|
package io.spring.application.profile;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import io.spring.application.ProfileQueryService;
|
import io.spring.application.ProfileQueryService;
|
||||||
import io.spring.application.data.ProfileData;
|
import io.spring.application.data.ProfileData;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
|
import io.spring.infrastructure.DbTestBase;
|
||||||
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
||||||
|
import java.util.Optional;
|
||||||
import org.junit.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Import;
|
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})
|
@Import({ProfileQueryService.class, MyBatisUserRepository.class})
|
||||||
public class ProfileQueryServiceTest {
|
public class ProfileQueryServiceTest extends DbTestBase {
|
||||||
@Autowired
|
@Autowired private ProfileQueryService profileQueryService;
|
||||||
private ProfileQueryService profileQueryService;
|
@Autowired private UserRepository userRepository;
|
||||||
@Autowired
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_fetch_profile_success() {
|
public void should_fetch_profile_success() {
|
||||||
@ -31,7 +24,8 @@ public class ProfileQueryServiceTest {
|
|||||||
User profileUser = new User("p@test.com", "p", "123", "", "");
|
User profileUser = new User("p@test.com", "p", "123", "", "");
|
||||||
userRepository.save(profileUser);
|
userRepository.save(profileUser);
|
||||||
|
|
||||||
Optional<ProfileData> optional = profileQueryService.findByUsername(profileUser.getUsername(), currentUser);
|
Optional<ProfileData> optional =
|
||||||
|
profileQueryService.findByUsername(profileUser.getUsername(), currentUser);
|
||||||
assertTrue(optional.isPresent());
|
assertTrue(optional.isPresent());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,19 +5,15 @@ import static org.junit.Assert.assertTrue;
|
|||||||
import io.spring.application.TagsQueryService;
|
import io.spring.application.TagsQueryService;
|
||||||
import io.spring.core.article.Article;
|
import io.spring.core.article.Article;
|
||||||
import io.spring.core.article.ArticleRepository;
|
import io.spring.core.article.ArticleRepository;
|
||||||
|
import io.spring.infrastructure.DbTestBase;
|
||||||
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.junit.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@MybatisTest
|
|
||||||
@Import({TagsQueryService.class, MyBatisArticleRepository.class})
|
@Import({TagsQueryService.class, MyBatisArticleRepository.class})
|
||||||
public class TagsQueryServiceTest {
|
public class TagsQueryServiceTest extends DbTestBase {
|
||||||
@Autowired private TagsQueryService tagsQueryService;
|
@Autowired private TagsQueryService tagsQueryService;
|
||||||
|
|
||||||
@Autowired private ArticleRepository articleRepository;
|
@Autowired private ArticleRepository articleRepository;
|
||||||
|
14
src/test/java/io/spring/infrastructure/DbTestBase.java
Normal file
14
src/test/java/io/spring/infrastructure/DbTestBase.java
Normal file
@ -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 {}
|
@ -13,11 +13,13 @@ import org.junit.runner.RunWith;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
@ActiveProfiles("test")
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@AutoConfigureTestDatabase
|
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
public class ArticleRepositoryTransactionTest {
|
public class ArticleRepositoryTransactionTest {
|
||||||
@Autowired private ArticleRepository articleRepository;
|
@Autowired private ArticleRepository articleRepository;
|
||||||
|
|
||||||
|
@ -10,22 +10,18 @@ import io.spring.core.article.ArticleRepository;
|
|||||||
import io.spring.core.article.Tag;
|
import io.spring.core.article.Tag;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
|
import io.spring.infrastructure.DbTestBase;
|
||||||
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
import io.spring.infrastructure.repository.MyBatisArticleRepository;
|
||||||
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
import io.spring.infrastructure.repository.MyBatisUserRepository;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
@MybatisTest
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@Import({MyBatisArticleRepository.class, MyBatisUserRepository.class})
|
@Import({MyBatisArticleRepository.class, MyBatisUserRepository.class})
|
||||||
public class MyBatisArticleRepositoryTest {
|
public class MyBatisArticleRepositoryTest extends DbTestBase {
|
||||||
@Autowired private ArticleRepository articleRepository;
|
@Autowired private ArticleRepository articleRepository;
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
package io.spring.infrastructure.comment;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@MybatisTest
|
import io.spring.core.comment.Comment;
|
||||||
@RunWith(SpringRunner.class)
|
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})
|
@Import({MyBatisCommentRepository.class})
|
||||||
public class MyBatisCommentRepositoryTest {
|
public class MyBatisCommentRepositoryTest extends DbTestBase {
|
||||||
@Autowired
|
@Autowired private CommentRepository commentRepository;
|
||||||
private CommentRepository commentRepository;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_create_and_fetch_comment_success() {
|
public void should_create_and_fetch_comment_success() {
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
package io.spring.infrastructure.favorite;
|
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.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
import io.spring.core.favorite.ArticleFavorite;
|
||||||
@MybatisTest
|
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})
|
@Import({MyBatisArticleFavoriteRepository.class})
|
||||||
public class MyBatisArticleFavoriteRepositoryTest {
|
public class MyBatisArticleFavoriteRepositoryTest extends DbTestBase {
|
||||||
@Autowired
|
@Autowired private ArticleFavoriteRepository articleFavoriteRepository;
|
||||||
private ArticleFavoriteRepository articleFavoriteRepository;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private io.spring.infrastructure.mybatis.mapper.ArticleFavoriteMapper articleFavoriteMapper;
|
private io.spring.infrastructure.mybatis.mapper.ArticleFavoriteMapper articleFavoriteMapper;
|
||||||
@ -27,7 +22,8 @@ public class MyBatisArticleFavoriteRepositoryTest {
|
|||||||
public void should_save_and_fetch_articleFavorite_success() {
|
public void should_save_and_fetch_articleFavorite_success() {
|
||||||
ArticleFavorite articleFavorite = new ArticleFavorite("123", "456");
|
ArticleFavorite articleFavorite = new ArticleFavorite("123", "456");
|
||||||
articleFavoriteRepository.save(articleFavorite);
|
articleFavoriteRepository.save(articleFavorite);
|
||||||
assertNotNull(articleFavoriteMapper.find(articleFavorite.getArticleId(), articleFavorite.getUserId()));
|
assertNotNull(
|
||||||
|
articleFavoriteMapper.find(articleFavorite.getArticleId(), articleFavorite.getUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,29 +1,23 @@
|
|||||||
package io.spring.infrastructure.user;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
import io.spring.core.user.FollowRelation;
|
||||||
@MybatisTest
|
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)
|
@Import(MyBatisUserRepository.class)
|
||||||
public class MyBatisUserRepositoryTest {
|
public class MyBatisUserRepositoryTest extends DbTestBase {
|
||||||
@Autowired
|
@Autowired private UserRepository userRepository;
|
||||||
private UserRepository userRepository;
|
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
Loading…
Reference in New Issue
Block a user