-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide ElementTagPostProcessor to handle element tag in plugin
Signed-off-by: JohnNiang <[email protected]>
- Loading branch information
Showing
10 changed files
with
212 additions
and
9 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
api/src/main/java/run/halo/app/theme/dialect/ElementTagPostProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package run.halo.app.theme.dialect; | ||
|
||
import org.pf4j.ExtensionPoint; | ||
import org.thymeleaf.context.ITemplateContext; | ||
import org.thymeleaf.model.IProcessableElementTag; | ||
import org.thymeleaf.processor.element.IElementTagStructureHandler; | ||
import reactor.core.publisher.Mono; | ||
|
||
/** | ||
* An extension point for post-processing element tag. | ||
* | ||
* @author johnniang | ||
* @since 2.20.0 | ||
*/ | ||
public interface ElementTagPostProcessor extends ExtensionPoint { | ||
|
||
/** | ||
* <p> | ||
* Execute the processor. | ||
* </p> | ||
* <p> | ||
* The {@link IProcessableElementTag} object argument is immutable, so all modifications to | ||
* this object or any | ||
* instructions to be given to the engine should be done through the specified | ||
* {@link IElementTagStructureHandler} handler. | ||
* </p> | ||
* | ||
* @param context the execution context. | ||
* @param tag the event this processor is executing on. | ||
* @param structureHandler the handler that will centralise modifications and commands to the | ||
* engine. | ||
* @return a {@link Mono} that will complete when processing finishes or empty mono if | ||
* not support. | ||
*/ | ||
Mono<Void> process( | ||
final ITemplateContext context, | ||
final IProcessableElementTag tag, | ||
final IElementTagStructureHandler structureHandler); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
application/src/main/java/run/halo/app/theme/dialect/ElementTagPostProcessors.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package run.halo.app.theme.dialect; | ||
|
||
import java.time.Duration; | ||
import lombok.extern.slf4j.Slf4j; | ||
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.templatemode.TemplateMode; | ||
import run.halo.app.plugin.extensionpoint.ExtensionGetter; | ||
|
||
/** | ||
* Element tag processors. | ||
* | ||
* @author johnniang | ||
* @since 2.20.0 | ||
*/ | ||
@Slf4j | ||
public class ElementTagPostProcessors extends AbstractElementTagProcessor { | ||
|
||
private static final int PRECEDENCE = Integer.MAX_VALUE; | ||
|
||
private final ExtensionGetter extensionGetter; | ||
|
||
public ElementTagPostProcessors(ExtensionGetter extensionGetter) { | ||
super(TemplateMode.HTML, | ||
null, | ||
null, | ||
false, | ||
null, | ||
false, | ||
PRECEDENCE); | ||
this.extensionGetter = extensionGetter; | ||
} | ||
|
||
@Override | ||
protected void doProcess( | ||
ITemplateContext context, | ||
IProcessableElementTag tag, | ||
IElementTagStructureHandler structureHandler | ||
) { | ||
extensionGetter.getExtensions(ElementTagPostProcessor.class) | ||
.concatMap(processor -> processor.process(context, tag, structureHandler) | ||
.doOnSuccess(v -> { | ||
if (log.isDebugEnabled()) { | ||
log.debug("Processed tag [{}] with processor [{}]", | ||
tag.getElementCompleteName(), processor.getClass().getName() | ||
); | ||
} | ||
}) | ||
) | ||
.then() | ||
.block(Duration.ofSeconds(20)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
application/src/test/java/run/halo/app/theme/dialect/ElementTagPostProcessorsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package run.halo.app.theme.dialect; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertInstanceOf; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.Mockito.doReturn; | ||
import static org.mockito.Mockito.inOrder; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.InjectMocks; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import org.thymeleaf.exceptions.TemplateProcessingException; | ||
import org.thymeleaf.model.IProcessableElementTag; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.publisher.Mono; | ||
import run.halo.app.plugin.extensionpoint.ExtensionGetter; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class ElementTagPostProcessorsTest { | ||
|
||
@Mock | ||
ExtensionGetter extensionGetter; | ||
|
||
@InjectMocks | ||
ElementTagPostProcessors elementTagPostProcessors; | ||
|
||
@Test | ||
void shouldDoNothingIfNoProcessorsFound() { | ||
when(extensionGetter.getExtensions(ElementTagPostProcessor.class)).thenReturn(Flux.empty()); | ||
elementTagPostProcessors.process(null, null, null); | ||
} | ||
|
||
@Test | ||
void shouldProcessWithOneProcessor() { | ||
var processor = mock(ElementTagPostProcessor.class); | ||
var tag = mock(IProcessableElementTag.class); | ||
doReturn(Mono.empty()).when(processor).process(null, tag, null); | ||
when(extensionGetter.getExtensions(ElementTagPostProcessor.class)) | ||
.thenReturn(Flux.just(processor)); | ||
elementTagPostProcessors.process(null, tag, null); | ||
} | ||
|
||
@Test | ||
void shouldProcessWithTwoProcessors() { | ||
var processor1 = mock(ElementTagPostProcessor.class); | ||
var processor2 = mock(ElementTagPostProcessor.class); | ||
var tag = mock(IProcessableElementTag.class); | ||
doReturn(Mono.empty()).when(processor1).process(null, tag, null); | ||
doReturn(Mono.empty()).when(processor2).process(null, tag, null); | ||
when(extensionGetter.getExtensions(ElementTagPostProcessor.class)) | ||
.thenReturn(Flux.just(processor1, processor2)); | ||
elementTagPostProcessors.process(null, tag, null); | ||
var inOrder = inOrder(processor1, processor2); | ||
inOrder.verify(processor1).process(null, tag, null); | ||
inOrder.verify(processor2).process(null, tag, null); | ||
} | ||
|
||
@Test | ||
void shouldProcessWithFailure() { | ||
var tag = mock(IProcessableElementTag.class); | ||
var processor1 = mock(ElementTagPostProcessor.class); | ||
var processor2 = mock(ElementTagPostProcessor.class); | ||
doReturn(Mono.error(new IllegalStateException("failed to process"))) | ||
.when(processor1).process(null, tag, null); | ||
when(extensionGetter.getExtensions(ElementTagPostProcessor.class)) | ||
.thenReturn(Flux.just(processor1, processor2)); | ||
|
||
var e = assertThrows(TemplateProcessingException.class, | ||
() -> elementTagPostProcessors.process(null, tag, null) | ||
); | ||
assertInstanceOf(IllegalStateException.class, e.getCause()); | ||
assertEquals("failed to process", e.getCause().getMessage()); | ||
verify(processor2, never()).process(null, tag, null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters