feat: theme template route filling necessary data to request model (#2393)

* feat: add archives data

* refactor: route strategy to populate necessary data

* refactor: next page url does not exceed the total number of pages
pull/2397/head^2
guqing 2022-09-09 14:43:02 +08:00 committed by GitHub
parent 9331f31d58
commit 1d73228b1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 584 additions and 39 deletions

View File

@ -66,7 +66,9 @@ public class PostFinderImpl implements PostFinder {
if (post == null) {
return null;
}
return getPostVo(post);
PostVo postVo = getPostVo(post);
postVo.setContent(content(postName));
return postVo;
}
@Override

View File

@ -57,6 +57,7 @@ public class SinglePageFinderImpl implements SinglePageFinder {
contributorFinder.getContributors(page.getStatus().getContributors());
SinglePageVo pageVo = SinglePageVo.from(page);
pageVo.setContributors(contributors);
pageVo.setContent(content(pageName));
return pageVo;
}

View File

@ -2,6 +2,7 @@ package run.halo.app.theme.finders.vo;
import java.util.List;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
/**
@ -11,6 +12,7 @@ import lombok.experimental.SuperBuilder;
* @since 2.0.0
*/
@Getter
@ToString
@SuperBuilder
public class BaseCategoryVo {
String name;

View File

@ -3,6 +3,7 @@ package run.halo.app.theme.finders.vo;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.util.Assert;
import run.halo.app.core.extension.Category;
@ -15,6 +16,7 @@ import run.halo.app.core.extension.Category;
*/
@Data
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CategoryTreeVo extends BaseCategoryVo {

View File

@ -18,6 +18,8 @@ import run.halo.app.core.extension.Post;
@EqualsAndHashCode(callSuper = true)
public class PostVo extends BasePostVo {
ContentVo content;
List<CategoryVo> categories;
List<TagVo> tags;
@ -53,6 +55,7 @@ public class PostVo extends BasePostVo {
.permalink(postStatus.getPermalink())
.excerpt(postStatus.getExcerpt())
.contributors(List.of())
.content(new ContentVo(null, null))
.build();
}
}

View File

@ -20,6 +20,8 @@ import run.halo.app.core.extension.SinglePage;
@EqualsAndHashCode(callSuper = true)
public class SinglePageVo extends BasePostVo {
ContentVo content;
/**
* Convert {@link SinglePage} to {@link SinglePageVo}.
*
@ -49,6 +51,7 @@ public class SinglePageVo extends BasePostVo {
.permalink(pageStatus.getPermalink())
.excerpt(pageStatus.getExcerpt())
.contributors(List.of())
.content(new ContentVo(null, null))
.build();
}
}

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
@ -35,13 +36,13 @@ public class TemplateRouteManager implements ApplicationListener<ApplicationRead
private final ReentrantReadWriteLock.WriteLock writeLock =
new ReentrantReadWriteLock().writeLock();
private final Map<String, RouterFunction<ServerResponse>> routerFunctionMap = new HashMap<>();
private final PermalinkIndexer permalinkIndexer;
private final PermalinkPatternProvider permalinkPatternProvider;
private final ApplicationContext applicationContext;
public TemplateRouteManager(PermalinkIndexer permalinkIndexer,
PermalinkPatternProvider permalinkPatternProvider) {
this.permalinkIndexer = permalinkIndexer;
public TemplateRouteManager(PermalinkPatternProvider permalinkPatternProvider,
ApplicationContext applicationContext) {
this.permalinkPatternProvider = permalinkPatternProvider;
this.applicationContext = applicationContext;
}
public Map<String, RouterFunction<ServerResponse>> getRouterFunctionMap() {
@ -85,6 +86,9 @@ public class TemplateRouteManager implements ApplicationListener<ApplicationRead
String pattern = getPatternByTemplateName(templateName);
RouterFunction<ServerResponse> routeFunction = templateRouterStrategy(templateName)
.getRouteFunction(templateName, pattern);
if (routeFunction == null) {
throw new IllegalStateException("Router function must not be null");
}
writeLock.lock();
try {
routerFunctionMap.put(templateName, routeFunction);
@ -106,15 +110,16 @@ public class TemplateRouteManager implements ApplicationListener<ApplicationRead
if (value == null) {
throw new NotFoundException("Unknown template: " + template);
}
return switch (value) {
case INDEX -> new IndexRouteStrategy();
case POST -> new PostRouteStrategy(permalinkIndexer);
case ARCHIVES -> new ArchivesRouteStrategy();
case TAGS -> new TagsRouteStrategy();
case TAG -> new TagRouteStrategy(permalinkIndexer);
case CATEGORIES -> new CategoriesRouteStrategy();
case CATEGORY -> new CategoryRouteStrategy(permalinkIndexer);
case SINGLE_PAGE -> new SinglePageRouteStrategy(permalinkIndexer);
case INDEX -> applicationContext.getBean(IndexRouteStrategy.class);
case POST -> applicationContext.getBean(PostRouteStrategy.class);
case ARCHIVES -> applicationContext.getBean(ArchivesRouteStrategy.class);
case TAGS -> applicationContext.getBean(TagsRouteStrategy.class);
case TAG -> applicationContext.getBean(TagRouteStrategy.class);
case CATEGORIES -> applicationContext.getBean(CategoriesRouteStrategy.class);
case CATEGORY -> applicationContext.getBean(CategoryRouteStrategy.class);
case SINGLE_PAGE -> applicationContext.getBean(SinglePageRouteStrategy.class);
};
}

View File

@ -1,7 +1,13 @@
package run.halo.app.theme.router;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import run.halo.app.extension.ListResult;
import run.halo.app.infra.utils.PathUtils;
/**
* The {@link TemplateRouterStrategy} for generate {@link RouterFunction} specific to the template.
@ -13,4 +19,86 @@ import org.springframework.web.reactive.function.server.ServerResponse;
public interface TemplateRouterStrategy {
RouterFunction<ServerResponse> getRouteFunction(String template, String pattern);
class PageUrlUtils {
public static final String PAGE_PART = "page";
public static int pageNum(ServerRequest request) {
String pageNum = request.pathVariables().get(PageUrlUtils.PAGE_PART);
return NumberUtils.toInt(pageNum, 1);
}
public static long totalPage(ListResult<?> list) {
return (list.getTotal() - 1) / list.getSize() + 1;
}
/**
* Gets next page url with path.
*
* @param path request path
* @return request path with next page part
*/
public static String nextPageUrl(String path, long total) {
String[] segments = StringUtils.split(path, "/");
long defaultPage = Math.min(2, Math.max(total, 1));
if (segments.length > 1) {
String pagePart = segments[segments.length - 2];
if (PAGE_PART.equals(pagePart)) {
int pageNumIndex = segments.length - 1;
String pageNum = segments[pageNumIndex];
segments[pageNumIndex] = toNextPage(pageNum, total);
return PathUtils.combinePath(segments);
}
return appendPagePart(PathUtils.combinePath(segments), defaultPage);
}
return appendPagePart(PathUtils.combinePath(segments), defaultPage);
}
/**
* Gets previous page url with path.
*
* @param path request path
* @return request path with previous page part
*/
public static String prevPageUrl(String path) {
String[] segments = StringUtils.split(path, "/");
if (segments.length > 1) {
String pagePart = segments[segments.length - 2];
if (PAGE_PART.equals(pagePart)) {
int pageNumIndex = segments.length - 1;
String pageNum = segments[pageNumIndex];
int prevPage = toPrevPage(pageNum);
segments[pageNumIndex] = String.valueOf(prevPage);
if (prevPage == 1) {
segments = ArrayUtils.subarray(segments, 0, pageNumIndex - 1);
}
if (segments.length == 0) {
return "/";
}
return PathUtils.combinePath(segments);
}
}
return StringUtils.defaultString(path, "/");
}
private static String appendPagePart(String path, long page) {
return PathUtils.combinePath(path, PAGE_PART, String.valueOf(page));
}
private static String toNextPage(String pageStr, long total) {
long page = Math.min(parseInt(pageStr) + 1, Math.max(total, 1));
return String.valueOf(page);
}
private static int toPrevPage(String pageStr) {
return Math.max(parseInt(pageStr) - 1, 1);
}
private static int parseInt(String pageStr) {
if (!NumberUtils.isParsable(pageStr)) {
throw new IllegalArgumentException("Page number must be a number");
}
return NumberUtils.toInt(pageStr, 1);
}
}
}

View File

@ -0,0 +1,84 @@
package run.halo.app.theme.router;
import java.util.List;
import lombok.Getter;
import lombok.ToString;
import run.halo.app.extension.ListResult;
/**
* Page wrapper with next and previous url.
*
* @param <T> the type of the list item.
* @author guqing
* @since 2.0.0
*/
@Getter
@ToString(callSuper = true)
public class UrlContextListResult<T> extends ListResult<T> {
private final String nextUrl;
private final String prevUrl;
public UrlContextListResult(int page, int size, long total, List<T> items, String nextUrl,
String prevUrl) {
super(page, size, total, items);
this.nextUrl = nextUrl;
this.prevUrl = prevUrl;
}
public static class Builder<T> {
private int page;
private int size;
private long total;
private List<T> items;
private String nextUrl;
private String prevUrl;
public Builder<T> page(int page) {
this.page = page;
return this;
}
public Builder<T> size(int size) {
this.size = size;
return this;
}
public Builder<T> total(long total) {
this.total = total;
return this;
}
public Builder<T> items(List<T> items) {
this.items = items;
return this;
}
public Builder<T> nextUrl(String nextUrl) {
this.nextUrl = nextUrl;
return this;
}
public Builder<T> prevUrl(String prevUrl) {
this.prevUrl = prevUrl;
return this;
}
/**
* Assign value with list result.
*
* @param listResult list result
* @return builder
*/
public Builder<T> listResult(ListResult<T> listResult) {
this.page = listResult.getPage();
this.size = listResult.getSize();
this.total = listResult.getTotal();
this.items = listResult.getItems();
return this;
}
public UrlContextListResult<T> build() {
return new UrlContextListResult<>(page, size, total, items, nextUrl, prevUrl);
}
}
}

View File

@ -2,14 +2,24 @@ package run.halo.app.theme.router.strategy;
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.router.TemplateRouterStrategy.PageUrlUtils.pageNum;
import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.totalPage;
import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.infra.utils.PathUtils;
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.TemplateRouterStrategy;
import run.halo.app.theme.router.UrlContextListResult;
/**
* The {@link ArchivesRouteStrategy} for generate {@link RouterFunction} specific to the template
@ -18,7 +28,13 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class ArchivesRouteStrategy implements TemplateRouterStrategy {
private final PostFinder postFinder;
public ArchivesRouteStrategy(PostFinder postFinder) {
this.postFinder = postFinder;
}
@Override
public RouterFunction<ServerResponse> getRouteFunction(String template, String prefix) {
@ -29,6 +45,18 @@ public class ArchivesRouteStrategy implements TemplateRouterStrategy {
.or(GET(PathUtils.combinePath(prefix, "/{year}/{month}/page/{page}")))
.and(accept(MediaType.TEXT_HTML)),
request -> ServerResponse.ok()
.render(DefaultTemplateEnum.ARCHIVES.getValue()));
.render(DefaultTemplateEnum.ARCHIVES.getValue(),
Map.of("posts", postList(request))));
}
private Mono<UrlContextListResult<PostVo>> postList(ServerRequest request) {
String path = request.path();
return Mono.defer(() -> Mono.just(postFinder.list(pageNum(request), 10)))
.publishOn(Schedulers.boundedElastic())
.map(list -> new UrlContextListResult.Builder<PostVo>()
.listResult(list)
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
.prevUrl(PageUrlUtils.prevPageUrl(path))
.build());
}
}

View File

@ -3,12 +3,19 @@ package run.halo.app.theme.router.strategy;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import java.util.List;
import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.infra.utils.PathUtils;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.CategoryFinder;
import run.halo.app.theme.finders.vo.CategoryTreeVo;
import run.halo.app.theme.router.TemplateRouterStrategy;
/**
@ -18,7 +25,13 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class CategoriesRouteStrategy implements TemplateRouterStrategy {
private final CategoryFinder categoryFinder;
public CategoriesRouteStrategy(CategoryFinder categoryFinder) {
this.categoryFinder = categoryFinder;
}
@Override
public RouterFunction<ServerResponse> getRouteFunction(String template, String prefix) {
@ -26,6 +39,12 @@ public class CategoriesRouteStrategy implements TemplateRouterStrategy {
.route(GET(PathUtils.combinePath(prefix))
.and(accept(MediaType.TEXT_HTML)),
request -> ServerResponse.ok()
.render(DefaultTemplateEnum.CATEGORIES.getValue()));
.render(DefaultTemplateEnum.CATEGORIES.getValue(),
Map.of("categories", categories())));
}
private Mono<List<CategoryTreeVo>> categories() {
return Mono.defer(() -> Mono.just(categoryFinder.listAsTree()))
.publishOn(Schedulers.boundedElastic());
}
}

View File

@ -2,19 +2,30 @@ package run.halo.app.theme.router.strategy;
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.router.TemplateRouterStrategy.PageUrlUtils.pageNum;
import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.totalPage;
import java.util.List;
import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.core.extension.Category;
import run.halo.app.extension.GroupVersionKind;
import run.halo.app.infra.utils.PathUtils;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.CategoryFinder;
import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.finders.vo.CategoryVo;
import run.halo.app.theme.finders.vo.PostVo;
import run.halo.app.theme.router.PermalinkIndexer;
import run.halo.app.theme.router.TemplateRouterStrategy;
import run.halo.app.theme.router.UrlContextListResult;
/**
* The {@link CategoryRouteStrategy} for generate {@link RouterFunction} specific to the template
@ -23,12 +34,19 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class CategoryRouteStrategy implements TemplateRouterStrategy {
private final PermalinkIndexer permalinkIndexer;
private final PostFinder postFinder;
public CategoryRouteStrategy(PermalinkIndexer permalinkIndexer) {
private final CategoryFinder categoryFinder;
public CategoryRouteStrategy(PermalinkIndexer permalinkIndexer, PostFinder postFinder,
CategoryFinder categoryFinder) {
this.permalinkIndexer = permalinkIndexer;
this.postFinder = postFinder;
this.categoryFinder = categoryFinder;
}
@Override
@ -47,7 +65,26 @@ public class CategoryRouteStrategy implements TemplateRouterStrategy {
String categoryName = permalinkIndexer.getNameBySlug(gvk, slug);
return ServerResponse.ok()
.render(DefaultTemplateEnum.CATEGORY.getValue(),
Map.of("name", categoryName));
Map.of("name", categoryName,
"posts", postListByCategoryName(categoryName, request),
"category", categoryByName(categoryName)));
});
}
private Mono<UrlContextListResult<PostVo>> postListByCategoryName(String name,
ServerRequest request) {
String path = request.path();
return Mono.defer(() -> Mono.just(postFinder.listByCategory(pageNum(request), 10, name)))
.publishOn(Schedulers.boundedElastic())
.map(list -> new UrlContextListResult.Builder<PostVo>()
.listResult(list)
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
.prevUrl(PageUrlUtils.prevPageUrl(path))
.build());
}
private Mono<CategoryVo> categoryByName(String name) {
return Mono.defer(() -> Mono.just(categoryFinder.getByName(name)))
.publishOn(Schedulers.boundedElastic());
}
}

View File

@ -2,13 +2,23 @@ package run.halo.app.theme.router.strategy;
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.router.TemplateRouterStrategy.PageUrlUtils.pageNum;
import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.totalPage;
import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
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.TemplateRouterStrategy;
import run.halo.app.theme.router.UrlContextListResult;
/**
* The {@link IndexRouteStrategy} for generate {@link RouterFunction} specific to the template
@ -17,14 +27,33 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class IndexRouteStrategy implements TemplateRouterStrategy {
private final PostFinder postFinder;
public IndexRouteStrategy(PostFinder postFinder) {
this.postFinder = postFinder;
}
@Override
public RouterFunction<ServerResponse> getRouteFunction(String template, String pattern) {
return RouterFunctions
.route(GET("/").or(GET("/page/{page}"))
.and(accept(MediaType.TEXT_HTML)),
request -> ServerResponse.ok()
.render(DefaultTemplateEnum.INDEX.getValue()));
.render(DefaultTemplateEnum.INDEX.getValue(),
Map.of("posts", postList(request))));
}
private Mono<UrlContextListResult<PostVo>> postList(ServerRequest request) {
String path = request.path();
return Mono.defer(() -> Mono.just(postFinder.list(pageNum(request), 10)))
.publishOn(Schedulers.boundedElastic())
.map(list -> new UrlContextListResult.Builder<PostVo>()
.listResult(list)
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
.prevUrl(PageUrlUtils.prevPageUrl(path))
.build());
}
}

View File

@ -10,6 +10,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.http.MediaType;
import org.springframework.http.server.PathContainer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
@ -17,10 +18,14 @@ import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.content.permalinks.ExtensionLocator;
import run.halo.app.core.extension.Post;
import run.halo.app.extension.GroupVersionKind;
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.PermalinkIndexer;
import run.halo.app.theme.router.TemplateRouterStrategy;
@ -31,11 +36,14 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class PostRouteStrategy implements TemplateRouterStrategy {
private final PermalinkIndexer permalinkIndexer;
private final PostFinder postFinder;
public PostRouteStrategy(PermalinkIndexer permalinkIndexer) {
public PostRouteStrategy(PermalinkIndexer permalinkIndexer, PostFinder postFinder) {
this.permalinkIndexer = permalinkIndexer;
this.postFinder = postFinder;
}
@Override
@ -86,17 +94,23 @@ public class PostRouteStrategy implements TemplateRouterStrategy {
PathPattern parse = PathPatternParser.defaultInstance.parse(pattern);
PathPattern.PathMatchInfo pathMatchInfo =
parse.matchAndExtract(PathContainer.parsePath(request.path()));
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put(PostRequestParamPredicate.NAME_PARAM,
Map<String, Object> model = new HashMap<>();
model.put(PostRequestParamPredicate.NAME_PARAM,
extensionLocator.name());
if (pathMatchInfo != null) {
uriVariables.putAll(pathMatchInfo.getUriVariables());
model.putAll(pathMatchInfo.getUriVariables());
}
model.put("post", postByName(extensionLocator.name()));
return ServerResponse.ok()
.render(DefaultTemplateEnum.POST.getValue(), uriVariables);
.render(DefaultTemplateEnum.POST.getValue(), model);
});
}
private Mono<PostVo> postByName(String name) {
return Mono.defer(() -> Mono.just(postFinder.getByName(name)))
.publishOn(Schedulers.boundedElastic());
}
class PostRequestParamPredicate {
static final String NAME_PARAM = "name";
static final String SLUG_PARAM = "slug";

View File

@ -7,14 +7,19 @@ import java.util.Map;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.core.extension.SinglePage;
import run.halo.app.extension.GroupVersionKind;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.SinglePageFinder;
import run.halo.app.theme.finders.vo.SinglePageVo;
import run.halo.app.theme.router.PermalinkIndexer;
import run.halo.app.theme.router.TemplateRouterStrategy;
@ -25,12 +30,16 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class SinglePageRouteStrategy implements TemplateRouterStrategy {
private final PermalinkIndexer permalinkIndexer;
private final SinglePageFinder singlePageFinder;
public SinglePageRouteStrategy(PermalinkIndexer permalinkIndexer) {
public SinglePageRouteStrategy(PermalinkIndexer permalinkIndexer,
SinglePageFinder singlePageFinder) {
this.permalinkIndexer = permalinkIndexer;
this.singlePageFinder = singlePageFinder;
}
@Override
@ -53,7 +62,14 @@ public class SinglePageRouteStrategy implements TemplateRouterStrategy {
return ServerResponse.notFound().build();
}
return ServerResponse.ok()
.render(DefaultTemplateEnum.SINGLE_PAGE.getValue(), Map.of("name", name));
.render(DefaultTemplateEnum.SINGLE_PAGE.getValue(),
Map.of("name", name,
"singlePage", singlePageByName(name)));
});
}
private Mono<SinglePageVo> singlePageByName(String name) {
return Mono.defer(() -> Mono.just(singlePageFinder.getByName(name)))
.publishOn(Schedulers.boundedElastic());
}
}

View File

@ -2,19 +2,30 @@ package run.halo.app.theme.router.strategy;
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.router.TemplateRouterStrategy.PageUrlUtils.pageNum;
import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.totalPage;
import java.util.List;
import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.core.extension.Tag;
import run.halo.app.extension.GroupVersionKind;
import run.halo.app.infra.utils.PathUtils;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.finders.TagFinder;
import run.halo.app.theme.finders.vo.PostVo;
import run.halo.app.theme.finders.vo.TagVo;
import run.halo.app.theme.router.PermalinkIndexer;
import run.halo.app.theme.router.TemplateRouterStrategy;
import run.halo.app.theme.router.UrlContextListResult;
/**
* The {@link TagRouteStrategy} for generate {@link RouterFunction} specific to the template
@ -23,12 +34,19 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class TagRouteStrategy implements TemplateRouterStrategy {
private final PermalinkIndexer permalinkIndexer;
private final PostFinder postFinder;
public TagRouteStrategy(PermalinkIndexer permalinkIndexer) {
private final TagFinder tagFinder;
public TagRouteStrategy(PermalinkIndexer permalinkIndexer, PostFinder postFinder,
TagFinder tagFinder) {
this.permalinkIndexer = permalinkIndexer;
this.postFinder = postFinder;
this.tagFinder = tagFinder;
}
@Override
@ -46,7 +64,27 @@ public class TagRouteStrategy implements TemplateRouterStrategy {
}
String name = permalinkIndexer.getNameBySlug(gvk, slug);
return ServerResponse.ok()
.render(DefaultTemplateEnum.TAG.getValue(), Map.of("name", name));
.render(DefaultTemplateEnum.TAG.getValue(),
Map.of("name", name,
"posts", postList(request, name),
"tag", tagByName(name))
);
});
}
private Mono<UrlContextListResult<PostVo>> postList(ServerRequest request, String name) {
String path = request.path();
return Mono.defer(() -> Mono.just(postFinder.listByTag(pageNum(request), 10, name)))
.publishOn(Schedulers.boundedElastic())
.map(list -> new UrlContextListResult.Builder<PostVo>()
.listResult(list)
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
.prevUrl(PageUrlUtils.prevPageUrl(path))
.build());
}
private Mono<TagVo> tagByName(String name) {
return Mono.defer(() -> Mono.just(tagFinder.getByName(name)))
.publishOn(Schedulers.boundedElastic());
}
}

View File

@ -3,12 +3,19 @@ package run.halo.app.theme.router.strategy;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import java.util.List;
import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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 reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import run.halo.app.infra.utils.PathUtils;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.TagFinder;
import run.halo.app.theme.finders.vo.TagVo;
import run.halo.app.theme.router.TemplateRouterStrategy;
/**
@ -18,8 +25,15 @@ import run.halo.app.theme.router.TemplateRouterStrategy;
* @author guqing
* @since 2.0.0
*/
@Component
public class TagsRouteStrategy implements TemplateRouterStrategy {
private final TagFinder tagFinder;
public TagsRouteStrategy(TagFinder tagFinder) {
this.tagFinder = tagFinder;
}
@Override
public RouterFunction<ServerResponse> getRouteFunction(String template, String prefix) {
String pattern = PathUtils.combinePath(prefix);
@ -27,6 +41,13 @@ public class TagsRouteStrategy implements TemplateRouterStrategy {
.route(GET(pattern)
.and(accept(MediaType.TEXT_HTML)),
request -> ServerResponse.ok()
.render(DefaultTemplateEnum.TAGS.getValue()));
.render(DefaultTemplateEnum.TAGS.getValue(),
Map.of("tags", tags()))
);
}
private Mono<List<TagVo>> tags() {
return Mono.defer(() -> Mono.just(tagFinder.listAll()))
.publishOn(Schedulers.boundedElastic());
}
}

View File

@ -1,6 +1,7 @@
package run.halo.app.theme.router;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import java.util.Map;
@ -8,10 +9,14 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationContext;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.TagFinder;
import run.halo.app.theme.router.strategy.TagsRouteStrategy;
/**
* Tests for {@link TemplateRouteManager}.
@ -26,16 +31,22 @@ class TemplateRouteManagerTest {
private PermalinkPatternProvider permalinkPatternProvider;
@Mock
private PermalinkIndexer permalinkIndexer;
private ApplicationContext applicationContext;
private TemplateRouteManager templateRouteManager;
@BeforeEach
void setUp() {
templateRouteManager = new TemplateRouteManager(permalinkIndexer, permalinkPatternProvider);
templateRouteManager = new TemplateRouteManager(permalinkPatternProvider,
applicationContext);
when(permalinkPatternProvider.getPattern(DefaultTemplateEnum.TAGS))
.thenReturn("/tags");
TagFinder tagFinder = Mockito.mock(TagFinder.class);
when(applicationContext.getBean(eq(TagsRouteStrategy.class)))
.thenReturn(new TagsRouteStrategy(tagFinder));
templateRouteManager.register(DefaultTemplateEnum.TAGS.getValue());
}

View File

@ -0,0 +1,68 @@
package run.halo.app.theme.router;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
/**
* Tests for {@link TemplateRouterStrategy}.
*
* @author guqing
* @since 2.0.0
*/
class TemplateRouterStrategyTest {
@Nested
class PageUrlUtils {
static String s = "/tags";
static String s1 = "/tags/page/1";
static String s2 = "/tags/page/2";
static String s3 = "/tags/y/m/page/2";
static String s4 = "/tags/y/m";
static String s5 = "/tags/y/m/page/3";
@Test
void nextPageUrl() {
long totalPage = 10;
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl(s, totalPage))
.isEqualTo("/tags/page/2");
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl(s2, totalPage))
.isEqualTo("/tags/page/3");
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl(s3, totalPage))
.isEqualTo("/tags/y/m/page/3");
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl(s4, totalPage))
.isEqualTo("/tags/y/m/page/2");
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl(s5, totalPage))
.isEqualTo("/tags/y/m/page/4");
// The number of pages does not exceed the total number of pages
totalPage = 1;
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl("/tags/page/1", totalPage))
.isEqualTo("/tags/page/1");
totalPage = 0;
assertThat(TemplateRouterStrategy.PageUrlUtils.nextPageUrl("/tags", totalPage))
.isEqualTo("/tags/page/1");
}
@Test
void prevPageUrl() {
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl(s))
.isEqualTo("/tags");
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl(s1))
.isEqualTo("/tags");
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl(s2))
.isEqualTo("/tags");
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl(s3))
.isEqualTo("/tags/y/m");
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl(s4))
.isEqualTo("/tags/y/m");
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl(s5))
.isEqualTo("/tags/y/m/page/2");
assertThat(TemplateRouterStrategy.PageUrlUtils.prevPageUrl("/page/2"))
.isEqualTo("/");
}
}
}

View File

@ -2,11 +2,14 @@ package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -17,6 +20,8 @@ import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.router.UrlContextListResult;
/**
* Tests for {@link ArchivesRouteStrategy}.
@ -29,12 +34,16 @@ class ArchivesRouteStrategyTest {
@Mock
private ViewResolver viewResolver;
@Mock
private PostFinder postFinder;
@InjectMocks
private ArchivesRouteStrategy archivesRouteStrategy;
@BeforeEach
void setUp() {
archivesRouteStrategy = new ArchivesRouteStrategy();
lenient().when(postFinder.list(any(), any())).thenReturn(
new UrlContextListResult<>(1, 10, 1, List.of(), null, null));
}
@Test

View File

@ -2,11 +2,14 @@ package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -17,6 +20,7 @@ import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.CategoryFinder;
/**
* Tests for {@link CategoriesRouteStrategy}.
@ -29,11 +33,16 @@ class CategoriesRouteStrategyTest {
@Mock
private ViewResolver viewResolver;
@Mock
private CategoryFinder categoryFinder;
@InjectMocks
private CategoriesRouteStrategy categoriesRouteStrategy;
@BeforeEach
void setUp() {
categoriesRouteStrategy = new CategoriesRouteStrategy();
lenient().when(categoryFinder.listAsTree())
.thenReturn(List.of());
}
@Test

View File

@ -1,13 +1,16 @@
package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -17,7 +20,9 @@ import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ListResult;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.router.PermalinkIndexer;
/**
@ -34,17 +39,23 @@ class CategoryRouteStrategyTest {
@Mock
private ViewResolver viewResolver;
@Mock
private PostFinder postFinder;
@InjectMocks
private CategoryRouteStrategy categoryRouteStrategy;
@BeforeEach
void setUp() {
categoryRouteStrategy = new CategoryRouteStrategy(permalinkIndexer);
when(permalinkIndexer.getSlugs(any()))
.thenReturn(List.of("category-slug-1", "category-slug-2"));
when(permalinkIndexer.getNameBySlug(any(), eq("category-slug-1")))
.thenReturn("category-name-1");
when(permalinkIndexer.getNameBySlug(any(), eq("category-slug-2")))
.thenReturn("category-name-2");
lenient().when(postFinder.listByCategory(anyInt(), anyInt(), any()))
.thenReturn(new ListResult<>(1, 10, 0, List.of()));
}
@Test

View File

@ -1,12 +1,16 @@
package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -16,7 +20,9 @@ import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ListResult;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder;
/**
* Tests for {@link IndexRouteStrategy}.
@ -29,11 +35,16 @@ class IndexRouteStrategyTest {
@Mock
private ViewResolver viewResolver;
@Mock
private PostFinder postFinder;
@InjectMocks
private IndexRouteStrategy indexRouteStrategy;
@BeforeEach
void setUp() {
indexRouteStrategy = new IndexRouteStrategy();
lenient().when(postFinder.list(anyInt(), anyInt()))
.thenReturn(new ListResult<>(1, 10, 0, List.of()));
}
@Test

View File

@ -9,6 +9,7 @@ import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -18,10 +19,13 @@ import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.content.TestPost;
import run.halo.app.content.permalinks.ExtensionLocator;
import run.halo.app.core.extension.Post;
import run.halo.app.extension.GroupVersionKind;
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.PermalinkIndexer;
/**
@ -39,11 +43,15 @@ class PostRouteStrategyTest {
@Mock
private PermalinkIndexer permalinkIndexer;
@Mock
private PostFinder postFinder;
@InjectMocks
private PostRouteStrategy postRouteStrategy;
@BeforeEach
void setUp() {
postRouteStrategy = new PostRouteStrategy(permalinkIndexer);
lenient().when(postFinder.getByName(any())).thenReturn(PostVo.from(TestPost.postV1()));
}
@Test

View File

@ -1,13 +1,16 @@
package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -17,7 +20,9 @@ import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ListResult;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.SinglePageFinder;
import run.halo.app.theme.router.PermalinkIndexer;
/**
@ -35,11 +40,16 @@ class SinglePageRouteStrategyTest {
@Mock
private ViewResolver viewResolver;
@Mock
private SinglePageFinder singlePageFinder;
@InjectMocks
private SinglePageRouteStrategy strategy;
@BeforeEach
void setUp() {
strategy = new SinglePageRouteStrategy(permalinkIndexer);
lenient().when(singlePageFinder.list(anyInt(), anyInt()))
.thenReturn(new ListResult<>(1, 10, 0, List.of()));
when(permalinkIndexer.getPermalinks(any()))
.thenReturn(List.of("/fake-slug"));
when(permalinkIndexer.getNameBySlug(any(), eq("fake-slug")))

View File

@ -1,13 +1,16 @@
package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -17,7 +20,9 @@ import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ListResult;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.router.PermalinkIndexer;
/**
@ -31,15 +36,18 @@ class TagRouteStrategyTest {
@Mock
private PermalinkIndexer permalinkIndexer;
@Mock
private PostFinder postFinder;
@Mock
private ViewResolver viewResolver;
@InjectMocks
private TagRouteStrategy tagRouteStrategy;
@BeforeEach
void setUp() {
tagRouteStrategy = new TagRouteStrategy(permalinkIndexer);
lenient().when(postFinder.listByTag(anyInt(), anyInt(), any()))
.thenReturn(new ListResult<>(1, 10, 0, List.of()));
when(permalinkIndexer.getSlugs(any()))
.thenReturn(List.of("fake-slug"));
when(permalinkIndexer.getNameBySlug(any(), eq("fake-slug")))

View File

@ -2,11 +2,14 @@ package run.halo.app.theme.router.strategy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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.http.HttpStatus;
@ -17,6 +20,7 @@ import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import reactor.core.publisher.Mono;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.TagFinder;
/**
* Tests for {@link TagsRouteStrategy}.
@ -30,11 +34,15 @@ class TagsRouteStrategyTest {
@Mock
private ViewResolver viewResolver;
@Mock
private TagFinder tagFinder;
@InjectMocks
private TagsRouteStrategy tagsRouteStrategy;
@BeforeEach
void setUp() {
tagsRouteStrategy = new TagsRouteStrategy();
lenient().when(tagFinder.listAll()).thenReturn(List.of());
}
@Test