refactor: optimize the usage of comment widget extension point (#4249)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.8.x

#### What this PR does / why we need it:
优化评论扩展点的使用方式

how to test it?
测试评论插件是否正常可用

#### Does this PR introduce a user-facing change?
```release-note
优化评论扩展点的使用方式
```
pull/4270/head
guqing 2023-07-20 16:59:56 +08:00 committed by GitHub
parent 5eb9b68209
commit 133e54106d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 26 deletions

View File

@ -1,11 +1,14 @@
package run.halo.app.plugin.extensionpoint;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.pf4j.ExtensionPoint;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -32,14 +35,17 @@ public class DefaultExtensionGetter implements ExtensionGetter {
.switchIfEmpty(Mono.just(ExtensionPointEnabled.EMPTY))
.mapNotNull(enabled -> {
var implClassNames = enabled.getOrDefault(extensionPoint.getName(), Set.of());
return pluginManager.getExtensions(extensionPoint)
List<T> allExtensions = getAllExtensions(extensionPoint);
if (allExtensions.isEmpty()) {
return null;
}
return allExtensions
.stream()
.filter(impl -> implClassNames.contains(impl.getClass().getName()))
.findFirst()
// Fallback to local implementation of the extension point.
// This will happen when no proper configuration is found.
.orElseGet(() ->
applicationContext.getBeanProvider(extensionPoint).getIfAvailable());
.orElseGet(() -> allExtensions.get(0));
});
}
@ -74,16 +80,23 @@ public class DefaultExtensionGetter implements ExtensionGetter {
if (type == ExtensionPointDefinition.ExtensionPointType.SINGLETON) {
return getEnabledExtension(extensionPoint).flux();
}
Stream<T> pluginExtsStream = pluginManager.getExtensions(extensionPoint)
.stream();
Stream<T> systemExtsStream = applicationContext.getBeanProvider(extensionPoint)
.orderedStream();
// TODO If the type is sortable, may need to process the returned order.
return Flux.just(pluginExtsStream, systemExtsStream)
.flatMap(Flux::fromStream);
return Flux.fromIterable(getAllExtensions(extensionPoint));
});
}
@NonNull
<T extends ExtensionPoint> List<T> getAllExtensions(Class<T> extensionPoint) {
Stream<T> pluginExtsStream = pluginManager.getExtensions(extensionPoint)
.stream();
Stream<T> systemExtsStream = applicationContext.getBeanProvider(extensionPoint)
.orderedStream();
return Stream.concat(systemExtsStream, pluginExtsStream)
.sorted(new AnnotationAwareOrderComparator())
.toList();
}
Mono<ExtensionPointDefinition> fetchExtensionPointDefinition(
Class<? extends ExtensionPoint> extensionPoint) {
// TODO Optimize query

View File

@ -1,6 +1,5 @@
package run.halo.app.theme.dialect;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IProcessableElementTag;
@ -8,7 +7,7 @@ import org.thymeleaf.processor.element.AbstractElementTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.spring6.context.SpringContextUtils;
import org.thymeleaf.templatemode.TemplateMode;
import run.halo.app.plugin.ExtensionComponentsFinder;
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
/**
@ -44,15 +43,15 @@ public class CommentElementTagProcessor extends AbstractElementTagProcessor {
protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
IElementTagStructureHandler structureHandler) {
final ApplicationContext appCtx = SpringContextUtils.getApplicationContext(context);
ExtensionComponentsFinder componentsFinder =
appCtx.getBean(ExtensionComponentsFinder.class);
List<CommentWidget> commentWidgets = componentsFinder.getExtensions(CommentWidget.class);
if (commentWidgets.isEmpty()) {
ExtensionGetter extensionGetter = appCtx.getBean(ExtensionGetter.class);
CommentWidget commentWidget =
extensionGetter.getEnabledExtensionByDefinition(CommentWidget.class)
.next()
.block();
if (commentWidget == null) {
structureHandler.replaceWith("", false);
return;
}
// TODO if find more than one comment widget, query CommentWidget setting to decide which
// one to use.
commentWidgets.get(0).render(context, tag, structureHandler);
commentWidget.render(context, tag, structureHandler);
}
}

View File

@ -18,7 +18,7 @@ spec:
className: run.halo.app.theme.ReactivePostContentHandler
displayName: ReactivePostContentHandler
type: MULTI_INSTANCE
description: "Provides a way to extend the post content to be displayed in the theme-side."
description: "Provides a way to extend the post content to be displayed on the theme-side."
---
apiVersion: plugin.halo.run/v1alpha1
@ -29,4 +29,15 @@ spec:
className: run.halo.app.theme.ReactiveSinglePageContentHandler
displayName: ReactiveSinglePageContentHandler
type: MULTI_INSTANCE
description: "Provides a way to extend the single page content to be displayed in the theme-side."
description: "Provides a way to extend the single page content to be displayed on the theme-side."
---
apiVersion: plugin.halo.run/v1alpha1
kind: ExtensionPointDefinition
metadata:
name: comment-widget
spec:
className: run.halo.app.theme.dialect.CommentWidget
displayName: CommentWidget
type: SINGLETON
description: "Provides an extension point for the comment widget on the theme-side."

View File

@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
@ -24,7 +23,9 @@ import org.thymeleaf.spring6.expression.ThymeleafEvaluationContext;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import org.thymeleaf.templateresource.ITemplateResource;
import org.thymeleaf.templateresource.StringTemplateResource;
import reactor.core.publisher.Flux;
import run.halo.app.plugin.ExtensionComponentsFinder;
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
/**
* Tests for {@link CommentElementTagProcessor}.
@ -41,7 +42,7 @@ class CommentElementTagProcessorTest {
private ApplicationContext applicationContext;
@Mock
private ExtensionComponentsFinder componentsFinder;
private ExtensionGetter extensionGetter;
private TemplateEngine templateEngine;
@ -51,14 +52,16 @@ class CommentElementTagProcessorTest {
templateEngine = new TemplateEngine();
templateEngine.setDialects(Set.of(haloProcessorDialect, new SpringStandardDialect()));
templateEngine.addTemplateResolver(new TestTemplateResolver());
when(applicationContext.getBean(eq(ExtensionComponentsFinder.class)))
.thenReturn(componentsFinder);
when(applicationContext.getBean(eq(ExtensionGetter.class)))
.thenReturn(extensionGetter);
}
@Test
void doProcess() {
Context context = getContext();
when(extensionGetter.getEnabledExtensionByDefinition(eq(CommentWidget.class)))
.thenReturn(Flux.empty());
String result = templateEngine.process("commentWidget", context);
assertThat(result).isEqualTo("""
<!DOCTYPE html>
@ -70,8 +73,8 @@ class CommentElementTagProcessorTest {
</html>
""");
when(componentsFinder.getExtensions(CommentWidget.class))
.thenReturn(List.of(new DefaultCommentWidget()));
when(extensionGetter.getEnabledExtensionByDefinition(eq(CommentWidget.class)))
.thenReturn(Flux.just(new DefaultCommentWidget()));
result = templateEngine.process("commentWidget", context);
assertThat(result).isEqualTo("""
<!DOCTYPE html>