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

Fallback-aware failure handling - make sure snippet with enabled fall… #5

Merged
merged 5 commits into from
Dec 12, 2018
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 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,3 +6,4 @@ List of changes that are finished but not yet released in any final version.

## 1.5.0
Initial open source release.
- [PR-5](https://github.com/Knotx/knotx-template-engine/pull/5) - implementation of [fallback handling](https://github.com/Cognifide/knotx/issues/466) in template engine
43 changes: 29 additions & 14 deletions core/src/main/java/io/knotx/te/core/TemplateEngineKnotProxy.java
Expand Up @@ -18,6 +18,7 @@
import io.knotx.dataobjects.ClientResponse;
import io.knotx.dataobjects.Fragment;
import io.knotx.dataobjects.KnotContext;
import io.knotx.exceptions.FragmentProcessingException;
import io.knotx.knot.AbstractKnotProxy;
import io.knotx.te.api.TemplateEngine;
import io.knotx.te.core.exception.UnsupportedEngineException;
Expand Down Expand Up @@ -54,26 +55,30 @@ protected Single<KnotContext> processRequest(KnotContext knotContext) {
.filter(fragment -> fragment.knots().contains(SUPPORTED_FRAGMENT_ID))
.doOnNext(this::traceFragment)
.map(fragment -> FragmentContext.from(fragment, options.getDefaultEngine()))
.map(
fragmentContext -> {
final TemplateEngine templateEngine = engines
.get(fragmentContext.getStrategy());
if (templateEngine != null) {
fragmentContext.fragment().content(
templateEngine
.process(fragmentContext.fragment()));
return fragmentContext;
} else {
throw new UnsupportedEngineException(
"No engine named '" + fragmentContext.getStrategy() + "' found.");
}
})
.flatMapSingle(this::processFragment)
.toList()
).orElse(Single.just(Collections.emptyList()))
.map(result -> createSuccessResponse(knotContext))
.onErrorReturn(error -> processError(knotContext, error));
}

protected Single<FragmentContext> processFragment(FragmentContext fc) {
return Single.just(fc)
.map(fragmentContext -> {
final TemplateEngine templateEngine = engines
.get(fragmentContext.getStrategy());
if (templateEngine != null) {
fragmentContext.fragment().content(
templateEngine
.process(fragmentContext.fragment()));
return fragmentContext;
} else {
throw new UnsupportedEngineException(
"No engine named '" + fragmentContext.getStrategy() + "' found.");
}
}).onErrorReturn(e -> handleFragmentError(fc, e));
}

@Override
protected boolean shouldProcess(Set<String> knots) {
return knots.contains(SUPPORTED_FRAGMENT_ID);
Expand Down Expand Up @@ -104,4 +109,14 @@ private void traceFragment(Fragment fragment) {
LOGGER.trace("Processing fragment {}", fragment.toJson().encodePrettily());
}
}

private FragmentContext handleFragmentError(FragmentContext fragmentContext, Throwable t) {
LOGGER.error("Fragment processing failed. Cause:{}\nFragmentContext:\n{}\n", t.getMessage(), fragmentContext);
fragmentContext.fragment().failure(SUPPORTED_FRAGMENT_ID, t);
if (fragmentContext.fragment().fallback().isPresent()) {
return fragmentContext;
} else {
throw new FragmentProcessingException(String.format("Fragment processing failed in %s", SUPPORTED_FRAGMENT_ID), t);
}
}
}
Expand Up @@ -3,6 +3,8 @@
import static io.knotx.junit5.util.RequestUtil.subscribeToResult_shouldSucceed;
import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;

import io.knotx.dataobjects.ClientRequest;
import io.knotx.dataobjects.Fragment;
Expand Down Expand Up @@ -37,11 +39,26 @@ void callTemplateEngine_validateResult(VertxTestContext context, Vertx vertx)
knotContext -> {
final String expectedMarkup = fileContentAsString("result/simple.txt");
final String markup = knotContext.getFragments().iterator().next().content();
final boolean failed = knotContext.getFragments().iterator().next().failed();

assertFalse(failed);
assertThat(markup, equalToIgnoringWhiteSpace(expectedMarkup));
});
}

@Test
@KnotxApplyConfiguration("templateEngineStack.conf")
void callTemplateEngine_validateFallback(VertxTestContext context, Vertx vertx)
throws IOException, URISyntaxException {

callWithAssertions(context, vertx, "snippet/simple-missing-engine.txt", "data/simple.json",
knotContext -> {
final boolean failed = knotContext.getFragments().iterator().next().failed();

assertTrue(failed);
});
}

private void callWithAssertions(
VertxTestContext context, Vertx vertx, String snippetPath, String dataPath,
Consumer<KnotContext> onSuccess) throws IOException, URISyntaxException {
Expand All @@ -63,7 +80,8 @@ private KnotContext buildContext(String snippetPath, String dataPath)
String fragmentContent = fileContentAsString(snippetPath);
JsonObject data = new JsonObject(fileContentAsString(dataPath));

final Fragment fragment = Fragment
final Fragment fragment = fragmentContent.contains("fallback")? Fragment
.snippet(Collections.singletonList("te"), fragmentContent, "BLANK") : Fragment
.snippet(Collections.singletonList("te"), fragmentContent);
fragment.context().mergeIn(new JsonObject(Collections.singletonMap("_result", data)));

Expand Down
7 changes: 7 additions & 0 deletions it-test/src/test/resources/snippet/simple-missing-engine.txt
@@ -0,0 +1,7 @@
<knotx:snippet knots="te"
te-strategy="missing-engine"
fallback="BLANK"
type="text/knotx-snippet">
<h2>Message - {{_result.message}}</h2>
<p>Info - {{_result.body.info}}</p>
</knotx:snippet>