Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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;
Expand All @@ -32,14 +35,17 @@ public <T extends ExtensionPoint> Mono<T> getEnabledExtension(Class<T> extension
.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));
});
}

Expand Down Expand Up @@ -74,16 +80,23 @@ public <T extends ExtensionPoint> Flux<T> getEnabledExtensionByDefinition(
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
Expand Down
@@ -1,14 +1,13 @@
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;
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;


/**
Expand Down Expand Up @@ -44,15 +43,15 @@ public CommentElementTagProcessor(final String dialectPrefix) {
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);
}
}
Expand Up @@ -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
Expand All @@ -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."
Expand Up @@ -4,7 +4,6 @@
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;
Expand All @@ -24,7 +23,9 @@
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}.
Expand All @@ -41,7 +42,7 @@ class CommentElementTagProcessorTest {
private ApplicationContext applicationContext;

@Mock
private ExtensionComponentsFinder componentsFinder;
private ExtensionGetter extensionGetter;

private TemplateEngine templateEngine;

Expand All @@ -51,14 +52,16 @@ void setUp() {
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>
Expand All @@ -70,8 +73,8 @@ void doProcess() {
</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>
Expand Down