mirror of https://github.com/halo-dev/halo
feat: add author pages for theme-side (#2923)
#### What type of PR is this? /kind feature /area core #### What this PR does / why we need it: 为主题端新增作者页面 see #2837 for more detail #### Which issue(s) this PR fixes: Fixes #2837 #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 主题端支持作者页面 ```pull/2942/head^2
parent
3033ceb1ec
commit
7a2f5d0a99
|
@ -71,6 +71,8 @@ public class User extends AbstractExtension {
|
|||
|
||||
private Instant lastLoginAt;
|
||||
|
||||
private String permalink;
|
||||
|
||||
private List<LoginHistory> loginHistories;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,30 +1,113 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.content.permalinks.ExtensionLocator;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.GroupVersionKind;
|
||||
import run.halo.app.extension.controller.Controller;
|
||||
import run.halo.app.extension.controller.ControllerBuilder;
|
||||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.extension.controller.Reconciler.Request;
|
||||
import run.halo.app.infra.AnonymousUserConst;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
import run.halo.app.infra.utils.PathUtils;
|
||||
import run.halo.app.theme.router.PermalinkIndexDeleteCommand;
|
||||
import run.halo.app.theme.router.PermalinkIndexUpdateCommand;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class UserReconciler implements Reconciler<Request> {
|
||||
|
||||
private static final String FINALIZER_NAME = "user-protection";
|
||||
private final ExtensionClient client;
|
||||
|
||||
public UserReconciler(ExtensionClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final ExternalUrlSupplier externalUrlSupplier;
|
||||
|
||||
@Override
|
||||
public Result reconcile(Request request) {
|
||||
//TODO Add reconciliation logic here for User extension.
|
||||
client.fetch(User.class, request.name()).ifPresent(user -> {
|
||||
if (user.getMetadata().getDeletionTimestamp() != null) {
|
||||
cleanUpResourcesAndRemoveFinalizer(request.name());
|
||||
return;
|
||||
}
|
||||
|
||||
addFinalizerIfNecessary(user);
|
||||
updatePermalink(request.name());
|
||||
});
|
||||
return new Result(false, null);
|
||||
}
|
||||
|
||||
private void updatePermalink(String name) {
|
||||
client.fetch(User.class, name).ifPresent(user -> {
|
||||
if (AnonymousUserConst.isAnonymousUser(name)) {
|
||||
// anonymous user is not allowed to have permalink
|
||||
return;
|
||||
}
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus(new User.UserStatus());
|
||||
}
|
||||
User.UserStatus status = user.getStatus();
|
||||
String oldPermalink = status.getPermalink();
|
||||
|
||||
status.setPermalink(getUserPermalink(user));
|
||||
|
||||
ExtensionLocator extensionLocator = getExtensionLocator(name);
|
||||
eventPublisher.publishEvent(
|
||||
new PermalinkIndexUpdateCommand(this, extensionLocator, status.getPermalink()));
|
||||
|
||||
if (!StringUtils.equals(oldPermalink, status.getPermalink())) {
|
||||
client.update(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static ExtensionLocator getExtensionLocator(String name) {
|
||||
return new ExtensionLocator(GroupVersionKind.fromExtension(User.class), name,
|
||||
name);
|
||||
}
|
||||
|
||||
private String getUserPermalink(User user) {
|
||||
return externalUrlSupplier.get()
|
||||
.resolve(PathUtils.combinePath("authors", user.getMetadata().getName()))
|
||||
.normalize().toString();
|
||||
}
|
||||
|
||||
private void addFinalizerIfNecessary(User oldUser) {
|
||||
Set<String> finalizers = oldUser.getMetadata().getFinalizers();
|
||||
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
|
||||
return;
|
||||
}
|
||||
client.fetch(User.class, oldUser.getMetadata().getName())
|
||||
.ifPresent(user -> {
|
||||
Set<String> newFinalizers = user.getMetadata().getFinalizers();
|
||||
if (newFinalizers == null) {
|
||||
newFinalizers = new HashSet<>();
|
||||
user.getMetadata().setFinalizers(newFinalizers);
|
||||
}
|
||||
newFinalizers.add(FINALIZER_NAME);
|
||||
client.update(user);
|
||||
});
|
||||
}
|
||||
|
||||
private void cleanUpResourcesAndRemoveFinalizer(String userName) {
|
||||
client.fetch(User.class, userName).ifPresent(user -> {
|
||||
eventPublisher.publishEvent(
|
||||
new PermalinkIndexDeleteCommand(this, getExtensionLocator(userName)));
|
||||
|
||||
if (user.getMetadata().getFinalizers() != null) {
|
||||
user.getMetadata().getFinalizers().remove(FINALIZER_NAME);
|
||||
}
|
||||
client.update(user);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller setupWith(ControllerBuilder builder) {
|
||||
return builder
|
||||
|
|
|
@ -4,4 +4,8 @@ public interface AnonymousUserConst {
|
|||
String PRINCIPAL = "anonymousUser";
|
||||
|
||||
String Role = "anonymous";
|
||||
|
||||
static boolean isAnonymousUser(String principal) {
|
||||
return PRINCIPAL.equals(principal);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ public enum DefaultTemplateEnum {
|
|||
|
||||
TAGS("tags"),
|
||||
|
||||
SINGLE_PAGE("page");
|
||||
SINGLE_PAGE("page"),
|
||||
|
||||
AUTHOR("author");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import java.util.List;
|
|||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.theme.finders.vo.Contributor;
|
||||
import run.halo.app.theme.finders.vo.ContributorVo;
|
||||
|
||||
/**
|
||||
* A finder for {@link User}.
|
||||
*/
|
||||
public interface ContributorFinder {
|
||||
|
||||
Mono<Contributor> getContributor(String name);
|
||||
Mono<ContributorVo> getContributor(String name);
|
||||
|
||||
Flux<Contributor> getContributors(List<String> names);
|
||||
Flux<ContributorVo> getContributors(List<String> names);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ public interface PostFinder {
|
|||
Mono<ListResult<ListedPostVo>> listByTag(@Nullable Integer page, @Nullable Integer size,
|
||||
String tag);
|
||||
|
||||
Mono<ListResult<ListedPostVo>> listByOwner(@Nullable Integer page, @Nullable Integer size,
|
||||
String owner);
|
||||
|
||||
Mono<ListResult<PostArchiveVo>> archives(Integer page, Integer size);
|
||||
|
||||
Mono<ListResult<PostArchiveVo>> archives(Integer page, Integer size, String year);
|
||||
|
|
|
@ -7,7 +7,7 @@ import run.halo.app.core.extension.User;
|
|||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.theme.finders.ContributorFinder;
|
||||
import run.halo.app.theme.finders.Finder;
|
||||
import run.halo.app.theme.finders.vo.Contributor;
|
||||
import run.halo.app.theme.finders.vo.ContributorVo;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link ContributorFinder}.
|
||||
|
@ -25,13 +25,13 @@ public class ContributorFinderImpl implements ContributorFinder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Mono<Contributor> getContributor(String name) {
|
||||
public Mono<ContributorVo> getContributor(String name) {
|
||||
return client.fetch(User.class, name)
|
||||
.map(Contributor::from);
|
||||
.map(ContributorVo::from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Contributor> getContributors(List<String> names) {
|
||||
public Flux<ContributorVo> getContributors(List<String> names) {
|
||||
if (names == null) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
|
|
@ -220,6 +220,12 @@ public class PostFinderImpl implements PostFinder {
|
|||
post -> contains(post.getSpec().getTags(), tag), defaultComparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ListResult<ListedPostVo>> listByOwner(Integer page, Integer size, String owner) {
|
||||
return listPost(page, size,
|
||||
post -> post.getSpec().getOwner().equals(owner), defaultComparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ListResult<PostArchiveVo>> archives(Integer page, Integer size) {
|
||||
return archives(page, size, null, null);
|
||||
|
|
|
@ -14,7 +14,7 @@ import run.halo.app.core.extension.User;
|
|||
@Value
|
||||
@ToString
|
||||
@Builder
|
||||
public class Contributor {
|
||||
public class ContributorVo {
|
||||
String name;
|
||||
|
||||
String displayName;
|
||||
|
@ -23,17 +23,22 @@ public class Contributor {
|
|||
|
||||
String bio;
|
||||
|
||||
String permalink;
|
||||
|
||||
/**
|
||||
* Convert {@link User} to {@link Contributor}.
|
||||
* Convert {@link User} to {@link ContributorVo}.
|
||||
*
|
||||
* @param user user extension
|
||||
* @return contributor value object
|
||||
*/
|
||||
public static Contributor from(User user) {
|
||||
public static ContributorVo from(User user) {
|
||||
User.UserStatus status = user.getStatus();
|
||||
String permalink = (status == null ? "" : status.getPermalink());
|
||||
return builder().name(user.getMetadata().getName())
|
||||
.displayName(user.getSpec().getDisplayName())
|
||||
.avatar(user.getSpec().getAvatar())
|
||||
.bio(user.getSpec().getBio())
|
||||
.permalink(permalink)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -31,9 +31,9 @@ public class ListedPostVo {
|
|||
|
||||
private List<TagVo> tags;
|
||||
|
||||
private List<Contributor> contributors;
|
||||
private List<ContributorVo> contributors;
|
||||
|
||||
private Contributor owner;
|
||||
private ContributorVo owner;
|
||||
|
||||
private StatsVo stats;
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@ public class ListedSinglePageVo {
|
|||
|
||||
private StatsVo stats;
|
||||
|
||||
private List<Contributor> contributors;
|
||||
private List<ContributorVo> contributors;
|
||||
|
||||
private Contributor owner;
|
||||
private ContributorVo owner;
|
||||
|
||||
/**
|
||||
* Convert {@link SinglePage} to {@link ListedSinglePageVo}.
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package run.halo.app.theme.finders.vo;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.MetadataOperator;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
public class UserVo {
|
||||
MetadataOperator metadata;
|
||||
|
||||
User.UserSpec spec;
|
||||
|
||||
User.UserStatus status;
|
||||
|
||||
/**
|
||||
* Converts to {@link UserVo} from {@link User}.
|
||||
*
|
||||
* @param user user extension
|
||||
* @return user value object.
|
||||
*/
|
||||
public static UserVo from(User user) {
|
||||
User.UserStatus statusCopy =
|
||||
JsonUtils.deepCopy(ObjectUtils.defaultIfNull(user.getStatus(), new User.UserStatus()));
|
||||
statusCopy.setLoginHistories(List.of());
|
||||
statusCopy.setLastLoginAt(null);
|
||||
|
||||
User.UserSpec userSpecCopy = JsonUtils.deepCopy(user.getSpec());
|
||||
userSpecCopy.setPassword("[PROTECTED]");
|
||||
return UserVo.builder()
|
||||
.metadata(user.getMetadata())
|
||||
.spec(userSpecCopy)
|
||||
.status(statusCopy)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ public class PermalinkPatternProvider {
|
|||
public String getPattern(DefaultTemplateEnum defaultTemplateEnum) {
|
||||
SystemSetting.ThemeRouteRules permalinkRules = getPermalinkRules();
|
||||
return switch (defaultTemplateEnum) {
|
||||
case INDEX, SINGLE_PAGE -> null;
|
||||
case INDEX, SINGLE_PAGE, AUTHOR -> null;
|
||||
case POST -> permalinkRules.getPost();
|
||||
case ARCHIVES -> permalinkRules.getArchives();
|
||||
case CATEGORY, CATEGORIES -> permalinkRules.getCategories();
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package run.halo.app.theme.router.strategy;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static run.halo.app.theme.router.PageUrlUtils.pageNum;
|
||||
import static run.halo.app.theme.router.PageUrlUtils.totalPage;
|
||||
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.GroupVersionKind;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
import run.halo.app.theme.finders.vo.UserVo;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
|
||||
/**
|
||||
* Author route strategy.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.1
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class AuthorRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
||||
|
||||
private final ReactiveExtensionClient client;
|
||||
|
||||
private final PostFinder postFinder;
|
||||
|
||||
private final SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
@Override
|
||||
public HandlerFunction<ServerResponse> getHandler(SystemSetting.ThemeRouteRules routeRules,
|
||||
String name) {
|
||||
return request -> ServerResponse.ok()
|
||||
.render(DefaultTemplateEnum.AUTHOR.getValue(),
|
||||
Map.of("name", name,
|
||||
"author", getByName(name),
|
||||
"posts", postList(request, name),
|
||||
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.AUTHOR.getValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private Mono<UrlContextListResult<ListedPostVo>> postList(ServerRequest request, String name) {
|
||||
String path = request.path();
|
||||
return environmentFetcher.fetchPost()
|
||||
.map(p -> defaultIfNull(p.getPostPageSize(), ModelConst.DEFAULT_PAGE_SIZE))
|
||||
.flatMap(pageSize -> postFinder.listByOwner(pageNum(request), pageSize, name))
|
||||
.map(list -> new UrlContextListResult.Builder<ListedPostVo>()
|
||||
.listResult(list)
|
||||
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
|
||||
.prevUrl(PageUrlUtils.prevPageUrl(path))
|
||||
.build());
|
||||
}
|
||||
|
||||
private Mono<UserVo> getByName(String name) {
|
||||
return client.fetch(User.class, name)
|
||||
.map(UserVo::from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(GroupVersionKind gvk) {
|
||||
return GroupVersionKind.fromExtension(User.class).equals(gvk);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.infra.AnonymousUserConst;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
import run.halo.app.theme.router.PermalinkIndexUpdateCommand;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserReconciler}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.1
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserReconcilerTest {
|
||||
@Mock
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Mock
|
||||
private ExternalUrlSupplier externalUrlSupplier;
|
||||
|
||||
@Mock
|
||||
private ExtensionClient client;
|
||||
|
||||
@InjectMocks
|
||||
private UserReconciler userReconciler;
|
||||
|
||||
@Test
|
||||
void permalinkForFakeUser() throws URISyntaxException {
|
||||
when(externalUrlSupplier.get()).thenReturn(new URI("http://localhost:8090"));
|
||||
|
||||
when(client.fetch(eq(User.class), eq("fake-user")))
|
||||
.thenReturn(Optional.of(user("fake-user")));
|
||||
userReconciler.reconcile(new Reconciler.Request("fake-user"));
|
||||
verify(client, times(1)).update(any(User.class));
|
||||
verify(eventPublisher, times(1)).publishEvent(any(PermalinkIndexUpdateCommand.class));
|
||||
|
||||
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
assertThat(captor.getValue().getStatus().getPermalink())
|
||||
.isEqualTo("http://localhost:8090/authors/fake-user");
|
||||
}
|
||||
|
||||
@Test
|
||||
void permalinkForAnonymousUser() {
|
||||
when(client.fetch(eq(User.class), eq(AnonymousUserConst.PRINCIPAL)))
|
||||
.thenReturn(Optional.of(user(AnonymousUserConst.PRINCIPAL)));
|
||||
userReconciler.reconcile(new Reconciler.Request(AnonymousUserConst.PRINCIPAL));
|
||||
verify(client, times(0)).update(any(User.class));
|
||||
verify(eventPublisher, times(0)).publishEvent(any(PermalinkIndexUpdateCommand.class));
|
||||
}
|
||||
|
||||
User user(String name) {
|
||||
User user = new User();
|
||||
user.setMetadata(new Metadata());
|
||||
user.getMetadata().setName(name);
|
||||
user.getMetadata().setFinalizers(Set.of("user-protection"));
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package run.halo.app.theme.finders.vo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserVo}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class UserVoTest {
|
||||
|
||||
@Test
|
||||
void from() throws JSONException {
|
||||
User user = new User();
|
||||
user.setMetadata(new Metadata());
|
||||
user.getMetadata().setName("fake-user");
|
||||
user.setSpec(new User.UserSpec());
|
||||
user.getSpec().setPassword("123456");
|
||||
user.getSpec().setEmail("example@example.com");
|
||||
user.getSpec().setAvatar("avatar");
|
||||
user.getSpec().setDisplayName("fake-user-display-name");
|
||||
user.getSpec().setBio("user bio");
|
||||
user.getSpec().setDisabled(false);
|
||||
user.getSpec().setPhone("123456789");
|
||||
user.getSpec().setRegisteredAt(Instant.parse("2022-01-01T00:00:00.00Z"));
|
||||
user.getSpec().setLoginHistoryLimit(5);
|
||||
user.getSpec().setTwoFactorAuthEnabled(false);
|
||||
|
||||
user.setStatus(new User.UserStatus());
|
||||
user.getStatus().setLastLoginAt(Instant.parse("2022-01-02T00:00:00.00Z"));
|
||||
User.LoginHistory loginHistory = new User.LoginHistory();
|
||||
loginHistory.setLoginAt(Instant.parse("2022-01-02T00:00:00.00Z"));
|
||||
loginHistory.setReason("login reason");
|
||||
loginHistory.setUserAgent("user agent");
|
||||
user.getStatus().setLoginHistories(List.of(loginHistory));
|
||||
|
||||
UserVo userVo = UserVo.from(user);
|
||||
JSONAssert.assertEquals("""
|
||||
{
|
||||
"metadata": {
|
||||
"name": "fake-user"
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "fake-user-display-name",
|
||||
"avatar": "avatar",
|
||||
"email": "example@example.com",
|
||||
"phone": "123456789",
|
||||
"password": "[PROTECTED]",
|
||||
"bio": "user bio",
|
||||
"registeredAt": "2022-01-01T00:00:00Z",
|
||||
"twoFactorAuthEnabled": false,
|
||||
"disabled": false,
|
||||
"loginHistoryLimit": 5
|
||||
},
|
||||
"status": {
|
||||
"loginHistories": []
|
||||
}
|
||||
}
|
||||
""",
|
||||
JsonUtils.objectToJson(userVo),
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromWhenStatusIsNull() {
|
||||
User user = new User();
|
||||
user.setMetadata(new Metadata());
|
||||
user.getMetadata().setName("fake-user");
|
||||
user.setSpec(new User.UserSpec());
|
||||
UserVo userVo = UserVo.from(user);
|
||||
|
||||
assertThat(userVo).isNotNull();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package run.halo.app.theme.router.strategy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthorRouteStrategy}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class AuthorRouteStrategyTest extends RouterStrategyTestSuite {
|
||||
|
||||
@Mock
|
||||
private ReactiveExtensionClient client;
|
||||
|
||||
@InjectMocks
|
||||
private AuthorRouteStrategy strategy;
|
||||
|
||||
@Test
|
||||
void handlerTest() {
|
||||
User user = new User();
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setName("fake-user");
|
||||
user.setMetadata(metadata);
|
||||
user.setSpec(new User.UserSpec());
|
||||
|
||||
when(client.fetch(eq(User.class), eq("fake-user"))).thenReturn(Mono.just(user));
|
||||
permalinkHttpGetRouter.insert("/authors/fake-user",
|
||||
strategy.getHandler(getThemeRouteRules(), "fake-user"));
|
||||
|
||||
when(viewResolver.resolveViewName(eq(DefaultTemplateEnum.AUTHOR.getValue()), any()))
|
||||
.thenReturn(Mono.just(new EmptyView() {
|
||||
@Override
|
||||
public Mono<Void> render(Map<String, ?> model, MediaType contentType,
|
||||
ServerWebExchange exchange) {
|
||||
assertThat(model.get("name")).isEqualTo("fake-user");
|
||||
assertThat(model.get("_templateId"))
|
||||
.isEqualTo(DefaultTemplateEnum.AUTHOR.getValue());
|
||||
assertThat(model.get("author")).isNotNull();
|
||||
assertThat(model.get("posts")).isNotNull();
|
||||
return Mono.empty();
|
||||
}
|
||||
}));
|
||||
|
||||
WebTestClient webTestClient = getWebTestClient(getRouterFunction());
|
||||
webTestClient.get()
|
||||
.uri("/authors/fake-user")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue