Initialize schemes before refreshing application context (#5032)

#### What type of PR is this?

/kind improvement
/area core

#### What this PR does / why we need it:

Prior to this proposal, we encountered an error requesting any page before Halo is in ready state. That is because the timing of schemes initialization is incorrect.

The current proposal is to advance schemes initialization before refreshing application and removes `SchemeInitializedEvent` because it cannot be listened by other beans.

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/4946

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?

```release-note
修复 Halo 还未处于准备就绪时访问页面或接口出现“Scheme not found”错误的问题
```
pull/5692/head^2
John Niang 2023-12-13 14:40:10 +08:00 committed by GitHub
parent 8b405faa57
commit d777dbf7ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 98 additions and 97 deletions

View File

@ -5,8 +5,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.function.server.ServerResponse;
import run.halo.app.extension.DefaultSchemeManager;
import run.halo.app.extension.DefaultSchemeWatcherManager;
import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.SchemeManager; import run.halo.app.extension.SchemeManager;
@ -19,18 +17,8 @@ public class ExtensionConfiguration {
@Bean @Bean
RouterFunction<ServerResponse> extensionsRouterFunction(ReactiveExtensionClient client, RouterFunction<ServerResponse> extensionsRouterFunction(ReactiveExtensionClient client,
SchemeWatcherManager watcherManager) { SchemeWatcherManager watcherManager, SchemeManager schemeManager) {
return new ExtensionCompositeRouterFunction(client, watcherManager); return new ExtensionCompositeRouterFunction(client, watcherManager, schemeManager);
}
@Bean
SchemeManager schemeManager(SchemeWatcherManager watcherManager) {
return new DefaultSchemeManager(watcherManager);
}
@Bean
SchemeWatcherManager schemeWatcherManager() {
return new DefaultSchemeWatcherManager();
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)

View File

@ -7,6 +7,7 @@ import java.util.Set;
import java.util.concurrent.locks.StampedLock; import java.util.concurrent.locks.StampedLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -19,7 +20,6 @@ import run.halo.app.extension.MetadataUtil;
import run.halo.app.extension.Unstructured; import run.halo.app.extension.Unstructured;
import run.halo.app.extension.Watcher; import run.halo.app.extension.Watcher;
import run.halo.app.extension.controller.RequestSynchronizer; import run.halo.app.extension.controller.RequestSynchronizer;
import run.halo.app.infra.SchemeInitializedEvent;
/** /**
* <p>Monitor changes to {@link Post} resources and establish a local, in-memory cache in an * <p>Monitor changes to {@link Post} resources and establish a local, in-memory cache in an
@ -33,7 +33,7 @@ import run.halo.app.infra.SchemeInitializedEvent;
* @since 2.0.0 * @since 2.0.0
*/ */
@Component @Component
public class PostIndexInformer implements ApplicationListener<SchemeInitializedEvent>, public class PostIndexInformer implements ApplicationListener<ApplicationStartedEvent>,
DisposableBean { DisposableBean {
public static final String TAG_POST_INDEXER = "tag-post-indexer"; public static final String TAG_POST_INDEXER = "tag-post-indexer";
public static final String LABEL_INDEXER_NAME = "post-label-indexer"; public static final String LABEL_INDEXER_NAME = "post-label-indexer";
@ -71,10 +71,6 @@ public class PostIndexInformer implements ApplicationListener<SchemeInitializedE
}; };
} }
public Set<String> getByIndex(String indexName, String indexKey) {
return postIndexer.getByIndex(indexName, indexKey);
}
public Set<String> getByTagName(String tagName) { public Set<String> getByTagName(String tagName) {
return postIndexer.getByIndex(TAG_POST_INDEXER, tagName); return postIndexer.getByIndex(TAG_POST_INDEXER, tagName);
} }
@ -104,10 +100,6 @@ public class PostIndexInformer implements ApplicationListener<SchemeInitializedE
return labelName + "=" + labelValue; return labelName + "=" + labelValue;
} }
public Set<String> getByLabel(String labelName, String labelValue) {
return postIndexer.getByIndex(LABEL_INDEXER_NAME, labelKey(labelName, labelValue));
}
@Override @Override
public void destroy() throws Exception { public void destroy() throws Exception {
if (postWatcher != null) { if (postWatcher != null) {
@ -119,7 +111,7 @@ public class PostIndexInformer implements ApplicationListener<SchemeInitializedE
} }
@Override @Override
public void onApplicationEvent(@NonNull SchemeInitializedEvent event) { public void onApplicationEvent(@NonNull ApplicationStartedEvent event) {
if (!synchronizer.isStarted()) { if (!synchronizer.isStarted()) {
synchronizer.start(); synchronizer.start();
} }

View File

@ -3,6 +3,9 @@ package run.halo.app.extension.router;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
@ -13,23 +16,31 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.Scheme; import run.halo.app.extension.Scheme;
import run.halo.app.extension.SchemeManager;
import run.halo.app.extension.SchemeWatcherManager; import run.halo.app.extension.SchemeWatcherManager;
import run.halo.app.extension.SchemeWatcherManager.SchemeWatcher; import run.halo.app.extension.SchemeWatcherManager.SchemeWatcher;
public class ExtensionCompositeRouterFunction implements public class ExtensionCompositeRouterFunction implements
RouterFunction<ServerResponse>, SchemeWatcher { RouterFunction<ServerResponse>,
SchemeWatcher,
InitializingBean,
ApplicationListener<ApplicationStartedEvent> {
private final Map<Scheme, RouterFunction<ServerResponse>> schemeRouterFuncMapper; private final Map<Scheme, RouterFunction<ServerResponse>> schemeRouterFuncMapper;
private final ReactiveExtensionClient client; private final ReactiveExtensionClient client;
private final SchemeManager schemeManager;
private final SchemeWatcherManager watcherManager;
public ExtensionCompositeRouterFunction(ReactiveExtensionClient client, public ExtensionCompositeRouterFunction(ReactiveExtensionClient client,
SchemeWatcherManager watcherManager) { SchemeWatcherManager watcherManager,
SchemeManager schemeManager) {
this.client = client; this.client = client;
this.schemeManager = schemeManager;
this.watcherManager = watcherManager;
schemeRouterFuncMapper = new ConcurrentHashMap<>(); schemeRouterFuncMapper = new ConcurrentHashMap<>();
if (watcherManager != null) {
watcherManager.register(this);
}
} }
@Override @Override
@ -60,4 +71,17 @@ public class ExtensionCompositeRouterFunction implements
this.schemeRouterFuncMapper.remove(unregisteredEvent.getDeletedScheme()); this.schemeRouterFuncMapper.remove(unregisteredEvent.getDeletedScheme());
} }
} }
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
schemeManager.schemes().forEach(scheme -> {
var factory = new ExtensionRouterFunctionFactory(scheme, client);
this.schemeRouterFuncMapper.put(scheme, factory.create());
});
}
@Override
public void afterPropertiesSet() {
watcherManager.register(this);
}
} }

View File

@ -2,6 +2,7 @@ package run.halo.app.infra;
import java.io.IOException; import java.io.IOException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DataBufferUtils;
@ -16,7 +17,7 @@ import run.halo.app.infra.utils.FileUtils;
@Slf4j @Slf4j
@Component @Component
public class DefaultThemeInitializer implements ApplicationListener<SchemeInitializedEvent> { public class DefaultThemeInitializer implements ApplicationListener<ApplicationStartedEvent> {
private final ThemeService themeService; private final ThemeService themeService;
@ -32,7 +33,7 @@ public class DefaultThemeInitializer implements ApplicationListener<SchemeInitia
} }
@Override @Override
public void onApplicationEvent(SchemeInitializedEvent event) { public void onApplicationEvent(ApplicationStartedEvent event) {
if (themeProps.getInitializer().isDisabled()) { if (themeProps.getInitializer().isDisabled()) {
log.debug("Skipped initializing default theme due to disabled"); log.debug("Skipped initializing default theme due to disabled");
return; return;

View File

@ -1,12 +1,14 @@
package run.halo.app.infra; package run.halo.app.infra;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener; import org.springframework.context.ApplicationListener;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -28,7 +30,7 @@ import run.halo.app.infra.utils.YamlUnstructuredLoader;
*/ */
@Slf4j @Slf4j
@Component @Component
public class ExtensionResourceInitializer { public class ExtensionResourceInitializer implements ApplicationListener<ApplicationStartedEvent> {
public static final Set<String> REQUIRED_EXTENSION_LOCATIONS = public static final Set<String> REQUIRED_EXTENSION_LOCATIONS =
Set.of("classpath:/extensions/*.yaml", "classpath:/extensions/*.yml"); Set.of("classpath:/extensions/*.yaml", "classpath:/extensions/*.yml");
@ -45,8 +47,7 @@ public class ExtensionResourceInitializer {
this.eventPublisher = eventPublisher; this.eventPublisher = eventPublisher;
} }
@EventListener(SchemeInitializedEvent.class) public void onApplicationEvent(ApplicationStartedEvent initializedEvent) {
public Mono<Void> initialize(SchemeInitializedEvent initializedEvent) {
var locations = new HashSet<String>(); var locations = new HashSet<String>();
if (!haloProperties.isRequiredExtensionDisabled()) { if (!haloProperties.isRequiredExtensionDisabled()) {
locations.addAll(REQUIRED_EXTENSION_LOCATIONS); locations.addAll(REQUIRED_EXTENSION_LOCATIONS);
@ -55,10 +56,10 @@ public class ExtensionResourceInitializer {
locations.addAll(haloProperties.getInitialExtensionLocations()); locations.addAll(haloProperties.getInitialExtensionLocations());
} }
if (CollectionUtils.isEmpty(locations)) { if (CollectionUtils.isEmpty(locations)) {
return Mono.empty(); return;
} }
return Flux.fromIterable(locations) Flux.fromIterable(locations)
.doOnNext(location -> .doOnNext(location ->
log.debug("Trying to initialize extension resources from location: {}", location)) log.debug("Trying to initialize extension resources from location: {}", location))
.map(this::listResources) .map(this::listResources)
@ -82,7 +83,8 @@ public class ExtensionResourceInitializer {
} }
}) })
.then(Mono.fromRunnable( .then(Mono.fromRunnable(
() -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this)))); () -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this))))
.block(Duration.ofMinutes(1));
} }
private Mono<Unstructured> createOrUpdate(Unstructured extension) { private Mono<Unstructured> createOrUpdate(Unstructured extension) {

View File

@ -1,11 +0,0 @@
package run.halo.app.infra;
import org.springframework.context.ApplicationEvent;
public class SchemeInitializedEvent extends ApplicationEvent {
public SchemeInitializedEvent(Object source) {
super(source);
}
}

View File

@ -1,7 +1,6 @@
package run.halo.app.infra; package run.halo.app.infra;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -36,7 +35,8 @@ import run.halo.app.core.extension.notification.Reason;
import run.halo.app.core.extension.notification.ReasonType; import run.halo.app.core.extension.notification.ReasonType;
import run.halo.app.core.extension.notification.Subscription; import run.halo.app.core.extension.notification.Subscription;
import run.halo.app.extension.ConfigMap; import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.SchemeManager; import run.halo.app.extension.DefaultSchemeManager;
import run.halo.app.extension.DefaultSchemeWatcherManager;
import run.halo.app.extension.Secret; import run.halo.app.extension.Secret;
import run.halo.app.migration.Backup; import run.halo.app.migration.Backup;
import run.halo.app.plugin.extensionpoint.ExtensionDefinition; import run.halo.app.plugin.extensionpoint.ExtensionDefinition;
@ -45,20 +45,17 @@ import run.halo.app.search.extension.SearchEngine;
import run.halo.app.security.PersonalAccessToken; import run.halo.app.security.PersonalAccessToken;
@Component @Component
public class SchemeInitializer implements ApplicationListener<ApplicationStartedEvent> { public class SchemeInitializer implements ApplicationListener<ApplicationContextInitializedEvent> {
private final SchemeManager schemeManager;
private final ApplicationEventPublisher eventPublisher;
public SchemeInitializer(SchemeManager schemeManager,
ApplicationEventPublisher eventPublisher) {
this.schemeManager = schemeManager;
this.eventPublisher = eventPublisher;
}
@Override @Override
public void onApplicationEvent(@NonNull ApplicationStartedEvent event) { public void onApplicationEvent(@NonNull ApplicationContextInitializedEvent event) {
var watcherManager = new DefaultSchemeWatcherManager();
var schemeManager = new DefaultSchemeManager(watcherManager);
var beanFactory = event.getApplicationContext().getBeanFactory();
beanFactory.registerSingleton("schemeWatcherManager", watcherManager);
beanFactory.registerSingleton("schemeManager", schemeManager);
schemeManager.register(Role.class); schemeManager.register(Role.class);
// plugin.halo.run // plugin.halo.run
@ -108,7 +105,5 @@ public class SchemeInitializer implements ApplicationListener<ApplicationStarted
schemeManager.register(Subscription.class); schemeManager.register(Subscription.class);
schemeManager.register(NotifierDescriptor.class); schemeManager.register(NotifierDescriptor.class);
schemeManager.register(Notification.class); schemeManager.register(Notification.class);
eventPublisher.publishEvent(new SchemeInitializedEvent(this));
} }
} }

View File

@ -1,12 +1,12 @@
package run.halo.app.search; package run.halo.app.search;
import java.util.concurrent.CountDownLatch; import java.time.Duration;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch; import org.springframework.util.StopWatch;
import run.halo.app.infra.SchemeInitializedEvent;
@Slf4j @Slf4j
@Component @Component
@ -19,17 +19,12 @@ public class IndicesInitializer {
} }
@Async @Async
@EventListener(SchemeInitializedEvent.class) @EventListener
public void whenSchemeInitialized(SchemeInitializedEvent event) throws InterruptedException { public void whenSchemeInitialized(ApplicationStartedEvent event) {
var latch = new CountDownLatch(1);
log.info("Initialize post indices..."); log.info("Initialize post indices...");
var watch = new StopWatch("PostIndicesWatch"); var watch = new StopWatch("PostIndicesWatch");
watch.start("rebuild"); watch.start("rebuild");
indicesService.rebuildPostIndices() indicesService.rebuildPostIndices().block(Duration.ofMinutes(5));
.doFinally(signalType -> latch.countDown())
.subscribe();
latch.await();
watch.stop();
log.info("Initialized post indices. Usage: {}", watch); log.info("Initialized post indices. Usage: {}", watch);
} }

View File

@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -15,7 +15,6 @@ import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.infra.SchemeInitializedEvent;
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
import run.halo.app.infra.SystemSetting; import run.halo.app.infra.SystemSetting;
import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.DefaultTemplateEnum;
@ -90,10 +89,15 @@ public class ThemeCompositeRouterFunction implements RouterFunction<ServerRespon
/** /**
* Refresh the {@link #cachedRouters} when the permalink rule is changed. * Refresh the {@link #cachedRouters} when the permalink rule is changed.
* *
* @param event {@link SchemeInitializedEvent} or {@link PermalinkRuleChangedEvent} * @param event {@link PermalinkRuleChangedEvent}
*/ */
@EventListener({SchemeInitializedEvent.class, PermalinkRuleChangedEvent.class}) @EventListener
public void onSchemeInitializedEvent(@NonNull ApplicationEvent event) { public void onPermalinkRuleChanged(PermalinkRuleChangedEvent event) {
this.cachedRouters = routerFunctions();
}
@EventListener
public void onApplicationStarted(ApplicationStartedEvent event) {
this.cachedRouters = routerFunctions(); this.cachedRouters = routerFunctions();
} }

View File

@ -0,0 +1 @@
org.springframework.context.ApplicationListener=run.halo.app.infra.SchemeInitializer

View File

@ -4,13 +4,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.reactive.function.server.HandlerStrategies;
@ -18,6 +19,7 @@ import org.springframework.web.reactive.function.server.ServerRequest;
import run.halo.app.extension.FakeExtension; import run.halo.app.extension.FakeExtension;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.Scheme; import run.halo.app.extension.Scheme;
import run.halo.app.extension.SchemeManager;
import run.halo.app.extension.SchemeWatcherManager; import run.halo.app.extension.SchemeWatcherManager;
import run.halo.app.extension.SchemeWatcherManager.SchemeRegistered; import run.halo.app.extension.SchemeWatcherManager.SchemeRegistered;
import run.halo.app.extension.SchemeWatcherManager.SchemeUnregistered; import run.halo.app.extension.SchemeWatcherManager.SchemeUnregistered;
@ -28,10 +30,17 @@ class ExtensionCompositeRouterFunctionTest {
@Mock @Mock
ReactiveExtensionClient client; ReactiveExtensionClient client;
@Mock
SchemeManager schemeManager;
@Mock
SchemeWatcherManager watcherManager;
@InjectMocks
ExtensionCompositeRouterFunction extensionRouterFunc;
@Test @Test
void shouldRouteWhenSchemeRegistered() { void shouldRouteWhenSchemeRegistered() {
var extensionRouterFunc = new ExtensionCompositeRouterFunction(client, null);
var exchange = MockServerWebExchange.from( var exchange = MockServerWebExchange.from(
MockServerHttpRequest.get("/apis/fake.halo.run/v1alpha1/fakes").build()); MockServerHttpRequest.get("/apis/fake.halo.run/v1alpha1/fakes").build());
@ -51,8 +60,6 @@ class ExtensionCompositeRouterFunctionTest {
@Test @Test
void shouldNotRouteWhenSchemeUnregistered() { void shouldNotRouteWhenSchemeUnregistered() {
var extensionRouterFunc = new ExtensionCompositeRouterFunction(client, null);
var exchange = MockServerWebExchange.from( var exchange = MockServerWebExchange.from(
MockServerHttpRequest.get("/apis/fake.halo.run/v1alpha1/fakes").build()); MockServerHttpRequest.get("/apis/fake.halo.run/v1alpha1/fakes").build());
@ -74,10 +81,16 @@ class ExtensionCompositeRouterFunctionTest {
} }
@Test @Test
void shouldRegisterWatcherIfWatcherManagerIsNotNull() { void shouldRegisterWatcherAfterPropertiesSet() {
var watcherManager = mock(SchemeWatcherManager.class); extensionRouterFunc.afterPropertiesSet();
var routerFunction = new ExtensionCompositeRouterFunction(client, watcherManager); verify(watcherManager).register(eq(extensionRouterFunc));
verify(watcherManager, times(1)).register(eq(routerFunction)); }
@Test
void shouldBuildRouterFunctionsOnApplicationStarted() {
var applicationStartedEvent = mock(ApplicationStartedEvent.class);
extensionRouterFunc.onApplicationEvent(applicationStartedEvent);
verify(schemeManager).schemes();
} }
} }

View File

@ -23,10 +23,10 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.FileSystemUtils; import org.springframework.util.FileSystemUtils;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.GroupVersionKind;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.Unstructured; import run.halo.app.extension.Unstructured;
@ -47,7 +47,7 @@ class ExtensionResourceInitializerTest {
@Mock @Mock
HaloProperties haloProperties; HaloProperties haloProperties;
@Mock @Mock
SchemeInitializedEvent applicationReadyEvent; ApplicationStartedEvent applicationStartedEvent;
@Mock @Mock
ApplicationEventPublisher eventPublisher; ApplicationEventPublisher eventPublisher;
@ -128,10 +128,7 @@ class ExtensionResourceInitializerTest {
.thenReturn(Mono.empty()); .thenReturn(Mono.empty());
when(extensionClient.create(any())).thenReturn(Mono.empty()); when(extensionClient.create(any())).thenReturn(Mono.empty());
var initializeMono = extensionResourceInitializer.initialize(applicationReadyEvent); extensionResourceInitializer.onApplicationEvent(applicationStartedEvent);
StepVerifier.create(initializeMono)
.verifyComplete();
verify(extensionClient, times(3)).create(argumentCaptor.capture()); verify(extensionClient, times(3)).create(argumentCaptor.capture());