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

View File

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

View File

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