mirror of https://github.com/halo-dev/halo
feat: support displaying private posts for owner on theme-side (#4412)
#### What type of PR is this? /kind feature /area core /milestone 2.9.x #### What this PR does / why we need it: 登录后支持在主题端展示作者的私有文章 how to test it? 1. 测试登录后是否能访问到自己创建的私有文章,退出登录后私有文章消失 2. 不能在在主题端看到别人创建的私有文章 3. 创建私有文章测试登录后使用主题端的上一页下一页功能是否正常 #### Which issue(s) this PR fixes: Fixes #3016 #### Does this PR introduce a user-facing change? ```release-note 登录后支持在主题端展示作者的私有文章 ```pull/4482/head
parent
5e21909e36
commit
637071b260
|
@ -1,7 +1,6 @@
|
|||
package run.halo.app.theme.finders;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import org.springframework.lang.NonNull;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
@ -13,10 +12,6 @@ import run.halo.app.theme.finders.vo.ListedPostVo;
|
|||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
|
||||
public interface PostPublicQueryService {
|
||||
Predicate<Post> FIXED_PREDICATE = post -> post.isPublished()
|
||||
&& Objects.equals(false, post.getSpec().getDeleted())
|
||||
&& Post.VisibleEnum.PUBLIC.equals(post.getSpec().getVisible());
|
||||
|
||||
|
||||
/**
|
||||
* Lists posts page by predicate and comparator.
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package run.halo.app.theme.finders.impl;
|
||||
|
||||
import static run.halo.app.theme.finders.PostPublicQueryService.FIXED_PREDICATE;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
|
@ -32,6 +30,7 @@ import run.halo.app.theme.finders.vo.NavigationPostVo;
|
|||
import run.halo.app.theme.finders.vo.PostArchiveVo;
|
||||
import run.halo.app.theme.finders.vo.PostArchiveYearMonthVo;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.ReactiveQueryPostPredicateResolver;
|
||||
|
||||
/**
|
||||
* A finder for {@link Post}.
|
||||
|
@ -43,17 +42,20 @@ import run.halo.app.theme.finders.vo.PostVo;
|
|||
@AllArgsConstructor
|
||||
public class PostFinderImpl implements PostFinder {
|
||||
|
||||
|
||||
private final ReactiveExtensionClient client;
|
||||
|
||||
private final PostPublicQueryService postPublicQueryService;
|
||||
|
||||
private final ReactiveQueryPostPredicateResolver postPredicateResolver;
|
||||
|
||||
@Override
|
||||
public Mono<PostVo> getByName(String postName) {
|
||||
return client.get(Post.class, postName)
|
||||
.filter(FIXED_PREDICATE)
|
||||
.flatMap(post -> postPublicQueryService.convertToVo(post,
|
||||
post.getSpec().getReleaseSnapshot())
|
||||
return postPredicateResolver.getPredicate()
|
||||
.flatMap(predicate -> client.get(Post.class, postName)
|
||||
.filter(predicate)
|
||||
.flatMap(post -> postPublicQueryService.convertToVo(post,
|
||||
post.getSpec().getReleaseSnapshot())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -65,7 +67,10 @@ public class PostFinderImpl implements PostFinder {
|
|||
@Override
|
||||
public Mono<NavigationPostVo> cursor(String currentName) {
|
||||
// TODO Optimize the post names query here
|
||||
return client.list(Post.class, FIXED_PREDICATE, defaultComparator())
|
||||
return postPredicateResolver.getPredicate()
|
||||
.flatMapMany(postPredicate ->
|
||||
client.list(Post.class, postPredicate, defaultComparator())
|
||||
)
|
||||
.map(post -> post.getMetadata().getName())
|
||||
.collectList()
|
||||
.flatMap(postNames -> Mono.just(NavigationPostVo.builder())
|
||||
|
@ -98,7 +103,8 @@ public class PostFinderImpl implements PostFinder {
|
|||
|
||||
@Override
|
||||
public Flux<ListedPostVo> listAll() {
|
||||
return client.list(Post.class, FIXED_PREDICATE, defaultComparator())
|
||||
return postPredicateResolver.getPredicate()
|
||||
.flatMapMany(predicate -> client.list(Post.class, predicate, defaultComparator()))
|
||||
.concatMap(postPublicQueryService::convertToListedVo);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import run.halo.app.theme.finders.vo.ContentVo;
|
|||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.finders.vo.StatsVo;
|
||||
import run.halo.app.theme.router.ReactiveQueryPostPredicateResolver;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
|
@ -48,13 +49,16 @@ public class PostPublicQueryServiceImpl implements PostPublicQueryService {
|
|||
|
||||
private final ExtensionGetter extensionGetter;
|
||||
|
||||
private final ReactiveQueryPostPredicateResolver postPredicateResolver;
|
||||
|
||||
@Override
|
||||
public Mono<ListResult<ListedPostVo>> list(Integer page, Integer size,
|
||||
Predicate<Post> postPredicate, Comparator<Post> comparator) {
|
||||
Predicate<Post> predicate = FIXED_PREDICATE
|
||||
.and(postPredicate == null ? post -> true : postPredicate);
|
||||
return client.list(Post.class, predicate,
|
||||
return postPredicateResolver.getPredicate()
|
||||
.map(predicate -> predicate.and(postPredicate == null ? post -> true : postPredicate))
|
||||
.flatMap(predicate -> client.list(Post.class, predicate,
|
||||
comparator, pageNullSafe(page), sizeNullSafe(size))
|
||||
)
|
||||
.flatMap(list -> Flux.fromStream(list.get())
|
||||
.concatMap(post -> convertToListedVo(post)
|
||||
.flatMap(postVo -> populateStats(postVo)
|
||||
|
@ -118,7 +122,6 @@ public class PostPublicQueryServiceImpl implements PostPublicQueryService {
|
|||
|
||||
@Override
|
||||
public Mono<PostVo> convertToVo(Post post, String snapshotName) {
|
||||
final String postName = post.getMetadata().getName();
|
||||
final String baseSnapshotName = post.getSpec().getBaseSnapshot();
|
||||
return convertToListedVo(post)
|
||||
.map(PostVo::from)
|
||||
|
@ -131,8 +134,10 @@ public class PostPublicQueryServiceImpl implements PostPublicQueryService {
|
|||
|
||||
@Override
|
||||
public Mono<ContentVo> getContent(String postName) {
|
||||
return client.get(Post.class, postName)
|
||||
.filter(FIXED_PREDICATE)
|
||||
return postPredicateResolver.getPredicate()
|
||||
.flatMap(predicate -> client.get(Post.class, postName)
|
||||
.filter(predicate)
|
||||
)
|
||||
.flatMap(post -> {
|
||||
String releaseSnapshot = post.getSpec().getReleaseSnapshot();
|
||||
return postService.getContent(releaseSnapshot, post.getSpec().getBaseSnapshot())
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.theme.finders.impl;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
@ -10,12 +11,15 @@ import java.util.function.Predicate;
|
|||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
import run.halo.app.core.extension.content.SinglePage;
|
||||
import run.halo.app.extension.ListResult;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.AnonymousUserConst;
|
||||
import run.halo.app.theme.finders.Finder;
|
||||
import run.halo.app.theme.finders.SinglePageConversionService;
|
||||
import run.halo.app.theme.finders.SinglePageFinder;
|
||||
|
@ -44,7 +48,7 @@ public class SinglePageFinderImpl implements SinglePageFinder {
|
|||
@Override
|
||||
public Mono<SinglePageVo> getByName(String pageName) {
|
||||
return client.get(SinglePage.class, pageName)
|
||||
.filter(FIXED_PREDICATE)
|
||||
.filterWhen(page -> queryPredicate().map(predicate -> predicate.test(page)))
|
||||
.flatMap(singlePagePublicQueryService::convertToVo);
|
||||
}
|
||||
|
||||
|
@ -78,6 +82,25 @@ public class SinglePageFinderImpl implements SinglePageFinder {
|
|||
.defaultIfEmpty(new ListResult<>(0, 0, 0, List.of()));
|
||||
}
|
||||
|
||||
Mono<Predicate<SinglePage>> queryPredicate() {
|
||||
Predicate<SinglePage> predicate = page -> page.isPublished()
|
||||
&& Objects.equals(false, page.getSpec().getDeleted());
|
||||
Predicate<SinglePage> visiblePredicate =
|
||||
page -> Post.VisibleEnum.PUBLIC.equals(page.getSpec().getVisible());
|
||||
return currentUserName()
|
||||
.map(username -> predicate.and(
|
||||
visiblePredicate.or(page -> username.equals(page.getSpec().getOwner())))
|
||||
)
|
||||
.defaultIfEmpty(predicate.and(visiblePredicate));
|
||||
}
|
||||
|
||||
Mono<String> currentUserName() {
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map(SecurityContext::getAuthentication)
|
||||
.map(Principal::getName)
|
||||
.filter(name -> !AnonymousUserConst.isAnonymousUser(name));
|
||||
}
|
||||
|
||||
static Comparator<SinglePage> defaultComparator() {
|
||||
Function<SinglePage, Boolean> pinned =
|
||||
page -> Objects.requireNonNullElse(page.getSpec().getPinned(), false);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
import run.halo.app.extension.ExtensionUtil;
|
||||
import run.halo.app.infra.AnonymousUserConst;
|
||||
|
||||
/**
|
||||
* The default implementation of {@link ReactiveQueryPostPredicateResolver}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.9.0
|
||||
*/
|
||||
@Component
|
||||
public class DefaultQueryPostPredicateResolver implements ReactiveQueryPostPredicateResolver {
|
||||
|
||||
@Override
|
||||
public Mono<Predicate<Post>> getPredicate() {
|
||||
Predicate<Post> predicate = post -> post.isPublished()
|
||||
&& !ExtensionUtil.isDeleted(post)
|
||||
&& Objects.equals(false, post.getSpec().getDeleted());
|
||||
Predicate<Post> visiblePredicate =
|
||||
post -> Post.VisibleEnum.PUBLIC.equals(post.getSpec().getVisible());
|
||||
return currentUserName()
|
||||
.map(username -> predicate.and(
|
||||
visiblePredicate.or(post -> username.equals(post.getSpec().getOwner())))
|
||||
)
|
||||
.defaultIfEmpty(predicate.and(visiblePredicate));
|
||||
}
|
||||
|
||||
Mono<String> currentUserName() {
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map(SecurityContext::getAuthentication)
|
||||
.map(Principal::getName)
|
||||
.filter(name -> !AnonymousUserConst.isAnonymousUser(name));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
|
||||
/**
|
||||
* The reactive query post predicate resolver.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface ReactiveQueryPostPredicateResolver {
|
||||
|
||||
Mono<Predicate<Post>> getPredicate();
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
@ -55,6 +56,10 @@ public class SinglePageRoute
|
|||
|
||||
private final ViewNameResolver viewNameResolver;
|
||||
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Mono<HandlerFunction<ServerResponse>> route(@NonNull ServerRequest request) {
|
||||
|
@ -144,6 +149,14 @@ public class SinglePageRoute
|
|||
|
||||
HandlerFunction<ServerResponse> handlerFunction(String name) {
|
||||
return request -> singlePageFinder.getByName(name)
|
||||
.doOnNext(singlePageVo -> {
|
||||
titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
singlePageVo.getSpec().getTitle(),
|
||||
singlePageVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale()
|
||||
);
|
||||
})
|
||||
.flatMap(singlePageVo -> {
|
||||
Map<String, Object> model = ModelMapUtils.singlePageModel(singlePageVo);
|
||||
String template = singlePageVo.getSpec().getTemplate();
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import java.util.Locale;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class TitleVisibilityIdentifyCalculator {
|
||||
|
||||
private final MessageSource messageSource;
|
||||
|
||||
/**
|
||||
* Calculate title with visibility identification.
|
||||
*
|
||||
* @param title title must not be null
|
||||
* @param visibleEnum visibility enum
|
||||
*/
|
||||
public String calculateTitle(String title, Post.VisibleEnum visibleEnum, Locale locale) {
|
||||
Assert.notNull(title, "Title must not be null");
|
||||
if (Post.VisibleEnum.PRIVATE.equals(visibleEnum)) {
|
||||
String identify = messageSource.getMessage(
|
||||
"title.visibility.identification.private",
|
||||
null,
|
||||
"",
|
||||
locale);
|
||||
return title + identify;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
|
@ -27,6 +28,7 @@ import run.halo.app.theme.finders.PostFinder;
|
|||
import run.halo.app.theme.finders.vo.PostArchiveVo;
|
||||
import run.halo.app.theme.router.ModelConst;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
|
||||
/**
|
||||
|
@ -44,6 +46,10 @@ public class ArchiveRouteFactory implements RouteFactory {
|
|||
|
||||
private final SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> create(String prefix) {
|
||||
RequestPredicate requestPredicate = patterns(prefix).stream()
|
||||
|
@ -83,6 +89,19 @@ public class ArchiveRouteFactory implements RouteFactory {
|
|||
return configuredPageSize(environmentFetcher, SystemSetting.Post::getArchivePageSize)
|
||||
.flatMap(pageSize -> postFinder.archives(pageNum, pageSize, variables.getYear(),
|
||||
variables.getMonth()))
|
||||
.doOnNext(list -> list.get()
|
||||
.map(PostArchiveVo::getMonths)
|
||||
.flatMap(List::stream)
|
||||
.flatMap(month -> month.getPosts().stream())
|
||||
.forEach(postVo -> postVo.getSpec()
|
||||
.setTitle(titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
postVo.getSpec().getTitle(),
|
||||
postVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale())
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(list -> new UrlContextListResult.Builder<PostArchiveVo>()
|
||||
.listResult(list)
|
||||
.nextUrl(PageUrlUtils.nextPageUrl(requestPath, totalPage(list)))
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
|
@ -25,6 +26,7 @@ import run.halo.app.theme.finders.vo.ListedPostVo;
|
|||
import run.halo.app.theme.finders.vo.UserVo;
|
||||
import run.halo.app.theme.router.ModelConst;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +44,10 @@ public class AuthorPostsRouteFactory implements RouteFactory {
|
|||
private final ReactiveExtensionClient client;
|
||||
private SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> create(String pattern) {
|
||||
return RouterFunctions
|
||||
|
@ -67,6 +73,17 @@ public class AuthorPostsRouteFactory implements RouteFactory {
|
|||
int pageNum = pageNumInPathVariable(request);
|
||||
return configuredPageSize(environmentFetcher, SystemSetting.Post::getPostPageSize)
|
||||
.flatMap(pageSize -> postFinder.listByOwner(pageNum, pageSize, name))
|
||||
.doOnNext(list -> {
|
||||
list.getItems().forEach(listedPostVo -> {
|
||||
listedPostVo.getSpec().setTitle(
|
||||
titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
listedPostVo.getSpec().getTitle(),
|
||||
listedPostVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale())
|
||||
);
|
||||
});
|
||||
})
|
||||
.map(list -> new UrlContextListResult.Builder<ListedPostVo>()
|
||||
.listResult(list)
|
||||
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Category;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
|
@ -27,6 +28,7 @@ import run.halo.app.theme.finders.vo.CategoryVo;
|
|||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
import run.halo.app.theme.router.ModelConst;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
|
@ -47,6 +49,10 @@ public class CategoryPostRouteFactory implements RouteFactory {
|
|||
private final ReactiveExtensionClient client;
|
||||
private final ViewNameResolver viewNameResolver;
|
||||
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> create(String prefix) {
|
||||
return RouterFunctions.route(GET(PathUtils.combinePath(prefix, "/{slug}"))
|
||||
|
@ -87,6 +93,15 @@ public class CategoryPostRouteFactory implements RouteFactory {
|
|||
int pageNum = pageNumInPathVariable(request);
|
||||
return configuredPageSize(environmentFetcher, SystemSetting.Post::getCategoryPageSize)
|
||||
.flatMap(pageSize -> postFinder.listByCategory(pageNum, pageSize, name))
|
||||
.doOnNext(list -> list.forEach(postVo -> postVo.getSpec().setTitle(
|
||||
titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
postVo.getSpec().getTitle(),
|
||||
postVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale()
|
||||
)
|
||||
)
|
||||
))
|
||||
.map(list -> new UrlContextListResult.Builder<ListedPostVo>()
|
||||
.listResult(list)
|
||||
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
|
@ -21,6 +22,7 @@ import run.halo.app.theme.finders.PostFinder;
|
|||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
import run.halo.app.theme.router.ModelConst;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
|
||||
/**
|
||||
|
@ -36,6 +38,8 @@ public class IndexRouteFactory implements RouteFactory {
|
|||
|
||||
private final PostFinder postFinder;
|
||||
private final SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> create(String pattern) {
|
||||
|
@ -54,8 +58,19 @@ public class IndexRouteFactory implements RouteFactory {
|
|||
|
||||
private Mono<UrlContextListResult<ListedPostVo>> postList(ServerRequest request) {
|
||||
String path = request.path();
|
||||
|
||||
return configuredPageSize(environmentFetcher, SystemSetting.Post::getPostPageSize)
|
||||
.flatMap(pageSize -> postFinder.list(pageNumInPathVariable(request), pageSize))
|
||||
.doOnNext(list -> list.getItems()
|
||||
.forEach(listedPostVo -> listedPostVo.getSpec()
|
||||
.setTitle(titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
listedPostVo.getSpec().getTitle(),
|
||||
listedPostVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale())
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(list -> new UrlContextListResult.Builder<ListedPostVo>()
|
||||
.listResult(list)
|
||||
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.theme.router.factories;
|
|||
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
import static run.halo.app.theme.finders.PostPublicQueryService.FIXED_PREDICATE;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
@ -14,6 +13,7 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
@ -26,6 +26,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
|
@ -37,6 +38,8 @@ import run.halo.app.theme.DefaultTemplateEnum;
|
|||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.ModelMapUtils;
|
||||
import run.halo.app.theme.router.ReactiveQueryPostPredicateResolver;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
|
@ -56,6 +59,12 @@ public class PostRouteFactory implements RouteFactory {
|
|||
|
||||
private final ReactiveExtensionClient client;
|
||||
|
||||
private final ReactiveQueryPostPredicateResolver queryPostPredicateResolver;
|
||||
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> create(String pattern) {
|
||||
PatternParser postParamPredicate =
|
||||
|
@ -73,7 +82,7 @@ public class PostRouteFactory implements RouteFactory {
|
|||
return request -> {
|
||||
Map<String, String> variables = mergedVariables(request);
|
||||
PostPatternVariable patternVariable = new PostPatternVariable();
|
||||
Optional.ofNullable(variables.get(paramPredicate.getQueryParamName()))
|
||||
Optional.ofNullable(variables.get(paramPredicate.getParamName()))
|
||||
.ifPresent(value -> {
|
||||
switch (paramPredicate.getPlaceholderName()) {
|
||||
case "name" -> patternVariable.setName(value);
|
||||
|
@ -98,6 +107,15 @@ public class PostRouteFactory implements RouteFactory {
|
|||
PostPatternVariable patternVariable) {
|
||||
Mono<PostVo> postVoMono = bestMatchPost(patternVariable);
|
||||
return postVoMono
|
||||
.doOnNext(postVo -> {
|
||||
postVo.getSpec().setTitle(
|
||||
titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
postVo.getSpec().getTitle(),
|
||||
postVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale())
|
||||
);
|
||||
})
|
||||
.flatMap(postVo -> {
|
||||
Map<String, Object> model = ModelMapUtils.postModel(postVo);
|
||||
String template = postVo.getSpec().getTemplate();
|
||||
|
@ -133,16 +151,19 @@ public class PostRouteFactory implements RouteFactory {
|
|||
}
|
||||
|
||||
private Flux<Post> fetchPostsByName(String name) {
|
||||
return client.fetch(Post.class, name)
|
||||
.filter(FIXED_PREDICATE)
|
||||
return queryPostPredicateResolver.getPredicate()
|
||||
.flatMap(predicate -> client.fetch(Post.class, name)
|
||||
.filter(predicate)
|
||||
)
|
||||
.flux();
|
||||
}
|
||||
|
||||
private Flux<Post> fetchPostsBySlug(String slug) {
|
||||
return client.list(Post.class,
|
||||
post -> FIXED_PREDICATE.test(post)
|
||||
&& matchIfPresent(slug, post.getSpec().getSlug()),
|
||||
null);
|
||||
return queryPostPredicateResolver.getPredicate()
|
||||
.flatMapMany(predicate -> client.list(Post.class,
|
||||
predicate.and(post -> matchIfPresent(slug, post.getSpec().getSlug())),
|
||||
null)
|
||||
);
|
||||
}
|
||||
|
||||
private boolean matchIfPresent(String variable, String target) {
|
||||
|
@ -175,6 +196,7 @@ public class PostRouteFactory implements RouteFactory {
|
|||
return mergedVariables;
|
||||
}
|
||||
|
||||
@Getter
|
||||
static class PatternParser {
|
||||
private static final Pattern PATTERN_COMPILE = Pattern.compile("([^&?]*)=\\{(.*?)\\}(&|$)");
|
||||
private static final Cache<String, Matcher> MATCHER_CACHE = CacheBuilder.newBuilder()
|
||||
|
@ -213,17 +235,5 @@ public class PostRouteFactory implements RouteFactory {
|
|||
|
||||
return RequestPredicates.queryParam(paramName, value -> true);
|
||||
}
|
||||
|
||||
public String getPlaceholderName() {
|
||||
return this.placeholderName;
|
||||
}
|
||||
|
||||
public String getQueryParamName() {
|
||||
return this.paramName;
|
||||
}
|
||||
|
||||
public boolean isQueryParamPattern() {
|
||||
return isQueryParamPattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.springframework.web.reactive.function.server.HandlerFunction;
|
|||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Tag;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
|
@ -25,6 +26,7 @@ import run.halo.app.theme.finders.TagFinder;
|
|||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
import run.halo.app.theme.finders.vo.TagVo;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +45,10 @@ public class TagPostRouteFactory implements RouteFactory {
|
|||
private final TagFinder tagFinder;
|
||||
private final PostFinder postFinder;
|
||||
|
||||
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
private final LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> create(String prefix) {
|
||||
return RouterFunctions
|
||||
|
@ -56,7 +62,17 @@ public class TagPostRouteFactory implements RouteFactory {
|
|||
.flatMap(tagVo -> {
|
||||
int pageNum = pageNumInPathVariable(request);
|
||||
String path = request.path();
|
||||
var postList = postList(tagVo.getMetadata().getName(), pageNum, path);
|
||||
var postList = postList(tagVo.getMetadata().getName(), pageNum, path)
|
||||
.doOnNext(list -> list.forEach(postVo ->
|
||||
postVo.getSpec().setTitle(
|
||||
titleVisibilityIdentifyCalculator.calculateTitle(
|
||||
postVo.getSpec().getTitle(),
|
||||
postVo.getSpec().getVisible(),
|
||||
localeContextResolver.resolveLocaleContext(request.exchange())
|
||||
.getLocale()
|
||||
)
|
||||
)
|
||||
));
|
||||
return ServerResponse.ok()
|
||||
.render(DefaultTemplateEnum.TAG.getValue(),
|
||||
Map.of("name", tagVo.getMetadata().getName(),
|
||||
|
|
|
@ -55,3 +55,5 @@ problemDetail.plugin.version.unsatisfied.requires=Plugin requires a minimum syst
|
|||
problemDetail.plugin.missingManifest=Missing plugin manifest file "plugin.yaml" or manifest file does not conform to the specification.
|
||||
problemDetail.internalServerError=Something went wrong, please try again later.
|
||||
problemDetail.migration.backup.notFound=The backup file does not exist or has been deleted.
|
||||
|
||||
title.visibility.identification.private=(Private)
|
|
@ -26,3 +26,5 @@ problemDetail.theme.version.unsatisfied.requires=主题要求一个最小的系
|
|||
problemDetail.theme.install.missingManifest=缺少 theme.yaml 配置文件或配置文件不符合规范。
|
||||
problemDetail.internalServerError=服务器内部发生错误,请稍候再试。
|
||||
problemDetail.migration.backup.notFound=备份文件不存在或已删除。
|
||||
|
||||
title.visibility.identification.private=(私有)
|
|
@ -4,13 +4,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static run.halo.app.theme.finders.PostPublicQueryService.FIXED_PREDICATE;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -32,6 +32,7 @@ import run.halo.app.theme.finders.TagFinder;
|
|||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
import run.halo.app.theme.finders.vo.PostArchiveVo;
|
||||
import run.halo.app.theme.finders.vo.PostArchiveYearMonthVo;
|
||||
import run.halo.app.theme.router.DefaultQueryPostPredicateResolver;
|
||||
|
||||
/**
|
||||
* Tests for {@link PostFinderImpl}.
|
||||
|
@ -77,7 +78,10 @@ class PostFinderImplTest {
|
|||
|
||||
@Test
|
||||
void predicate() {
|
||||
List<String> strings = posts().stream().filter(FIXED_PREDICATE)
|
||||
Predicate<Post> predicate = new DefaultQueryPostPredicateResolver().getPredicate().block();
|
||||
assertThat(predicate).isNotNull();
|
||||
|
||||
List<String> strings = posts().stream().filter(predicate)
|
||||
.map(post -> post.getMetadata().getName())
|
||||
.toList();
|
||||
assertThat(strings).isEqualTo(List.of("post-1", "post-2", "post-6"));
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
|
||||
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import reactor.test.StepVerifier;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
import run.halo.app.extension.Metadata;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveQueryPostPredicateResolver}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.9.0
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
class ReactiveQueryPostPredicateResolverTest {
|
||||
|
||||
private ReactiveQueryPostPredicateResolver postPredicateResolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
postPredicateResolver = new DefaultQueryPostPredicateResolver();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPredicateWithoutAuth() {
|
||||
postPredicateResolver.getPredicate()
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(predicate -> {
|
||||
Post post = new Post();
|
||||
post.setMetadata(new Metadata());
|
||||
post.getMetadata().setName("fake-post");
|
||||
|
||||
post.setSpec(new Post.PostSpec());
|
||||
post.getSpec().setDeleted(false);
|
||||
post.getMetadata().setLabels(Map.of(Post.PUBLISHED_LABEL, "true"));
|
||||
post.getSpec().setVisible(Post.VisibleEnum.PRIVATE);
|
||||
assertThat(predicate.test(post)).isFalse();
|
||||
|
||||
post.getSpec().setVisible(Post.VisibleEnum.PUBLIC);
|
||||
assertThat(predicate.test(post)).isTrue();
|
||||
|
||||
post.getMetadata().setLabels(Map.of(Post.PUBLISHED_LABEL, "false"));
|
||||
assertThat(predicate.test(post)).isFalse();
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "halo")
|
||||
void getPredicateWithAuth() {
|
||||
postPredicateResolver.getPredicate()
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(predicate -> {
|
||||
Post post = new Post();
|
||||
post.setMetadata(new Metadata());
|
||||
post.getMetadata().setName("fake-post");
|
||||
|
||||
post.setSpec(new Post.PostSpec());
|
||||
post.getSpec().setDeleted(false);
|
||||
post.getSpec().setOwner("halo");
|
||||
post.getMetadata().setLabels(Map.of(Post.PUBLISHED_LABEL, "true"));
|
||||
post.getSpec().setVisible(Post.VisibleEnum.PRIVATE);
|
||||
assertThat(predicate.test(post)).isTrue();
|
||||
|
||||
post.getSpec().setOwner("guqing");
|
||||
assertThat(predicate.test(post)).isFalse();
|
||||
|
||||
post.getSpec().setOwner("halo");
|
||||
post.getSpec().setVisible(Post.VisibleEnum.PUBLIC);
|
||||
assertThat(predicate.test(post)).isTrue();
|
||||
|
||||
post.getSpec().setDeleted(true);
|
||||
assertThat(predicate.test(post)).isFalse();
|
||||
|
||||
post.getSpec().setVisible(Post.VisibleEnum.INTERNAL);
|
||||
assertThat(predicate.test(post)).isFalse();
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import static org.mockito.Mockito.when;
|
|||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
|
@ -21,6 +22,7 @@ import org.mockito.InjectMocks;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.context.i18n.SimpleLocaleContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -35,6 +37,7 @@ import org.springframework.web.reactive.function.server.RouterFunctions;
|
|||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
@ -69,6 +72,12 @@ class SinglePageRouteTest {
|
|||
@Mock
|
||||
ExtensionClient client;
|
||||
|
||||
@Mock
|
||||
LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Mock
|
||||
TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
@InjectMocks
|
||||
SinglePageRoute singlePageRoute;
|
||||
|
||||
|
@ -115,6 +124,8 @@ class SinglePageRouteTest {
|
|||
.build())
|
||||
.build();
|
||||
|
||||
when(localeContextResolver.resolveLocaleContext(any()))
|
||||
.thenReturn(new SimpleLocaleContext(Locale.getDefault()));
|
||||
webTestClient.get()
|
||||
.uri("/archives/fake-name")
|
||||
.exchange()
|
||||
|
|
|
@ -5,17 +5,20 @@ import static org.mockito.ArgumentMatchers.any;
|
|||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.context.i18n.SimpleLocaleContext;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.content.TestPost;
|
||||
import run.halo.app.core.extension.content.Post;
|
||||
|
@ -25,8 +28,11 @@ import run.halo.app.extension.ReactiveExtensionClient;
|
|||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.DefaultQueryPostPredicateResolver;
|
||||
import run.halo.app.theme.router.EmptyView;
|
||||
import run.halo.app.theme.router.ModelConst;
|
||||
import run.halo.app.theme.router.ReactiveQueryPostPredicateResolver;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
|
@ -47,6 +53,15 @@ class PostRouteFactoryTest extends RouteFactoryTestSuite {
|
|||
@Mock
|
||||
private ReactiveExtensionClient client;
|
||||
|
||||
@Mock
|
||||
private ReactiveQueryPostPredicateResolver predicateResolver;
|
||||
|
||||
@Mock
|
||||
private LocaleContextResolver localeContextResolver;
|
||||
|
||||
@Mock
|
||||
private TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
|
||||
|
||||
@InjectMocks
|
||||
private PostRouteFactory postRouteFactory;
|
||||
|
||||
|
@ -64,10 +79,14 @@ class PostRouteFactoryTest extends RouteFactoryTestSuite {
|
|||
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(), any(), any()))
|
||||
.thenReturn(Mono.just(DefaultTemplateEnum.POST.getValue()));
|
||||
when(predicateResolver.getPredicate())
|
||||
.thenReturn(new DefaultQueryPostPredicateResolver().getPredicate());
|
||||
|
||||
RouterFunction<ServerResponse> routerFunction = postRouteFactory.create("/archives/{name}");
|
||||
WebTestClient webTestClient = getWebTestClient(routerFunction);
|
||||
|
||||
when(localeContextResolver.resolveLocaleContext(any()))
|
||||
.thenReturn(new SimpleLocaleContext(Locale.getDefault()));
|
||||
when(viewResolver.resolveViewName(any(), any()))
|
||||
.thenReturn(Mono.just(new EmptyView() {
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue