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 4 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 @@ -3,6 +3,7 @@ All notable changes to `knotx-template-engine` will be documented in this file.

## Unreleased
List of changes that are finished but not yet released in any final version.
- [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

## 1.5.0
Initial open source release.
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>