Skip to content

Commit 972fba6

Browse files
authored
Merge pull request #50383 from mkouba/issue-50377
Qute: fix error propagation from section helpers
2 parents ede20c5 + 4f44ee5 commit 972fba6

File tree

6 files changed

+93
-12
lines changed

6 files changed

+93
-12
lines changed

independent-projects/qute/core/src/main/java/io/quarkus/qute/CompletedStage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public boolean isFailure() {
6161

6262
public T get() {
6363
if (exception != null) {
64+
if (exception instanceof TemplateException te) {
65+
throw te;
66+
}
6467
// Always wrap the original exception if completed exceptionally
6568
throw new TemplateException(exception);
6669
}

independent-projects/qute/core/src/main/java/io/quarkus/qute/IfSectionHelper.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ private DoubletonCondition(Condition condition1, Condition condition2, Operator
290290
@Override
291291
public CompletionStage<Object> evaluate(SectionResolutionContext context) {
292292
CompletionStage<Object> ret = condition1.evaluate(context);
293-
if (ret instanceof CompletedStage completed) {
294-
ret = evaluateSecond(context, completed.get());
293+
if (ret instanceof CompletedStage<Object> completed) {
294+
ret = completed.isFailure() ? completed : evaluateSecond(context, completed.get());
295295
} else {
296296
ret = ret.thenCompose(first -> evaluateSecond(context, first));
297297
}
@@ -323,8 +323,8 @@ CompletionStage<Object> evaluateSecond(SectionResolutionContext context, Object
323323
return CompletedStage.of(shortResult);
324324
}
325325
CompletionStage<Object> second = condition2.evaluate(context);
326-
if (second instanceof CompletedStage completed) {
327-
return processValues(operator, firstVal, completed.get());
326+
if (second instanceof CompletedStage<Object> completed) {
327+
return completed.isFailure() ? completed : processValues(operator, firstVal, completed.get());
328328
} else {
329329
return second.thenCompose(c2 -> processValues(operator, firstVal, c2));
330330
}
@@ -395,9 +395,10 @@ CompletionStage<Object> evaluateNext(SectionResolutionContext context, Object pr
395395
return processConditionValue(context, operator, previousValue, literalVal, iter);
396396
} else {
397397
CompletionStage<Object> ret = next.evaluate(context);
398-
if (ret instanceof CompletedStage completed) {
399-
return processConditionValue(context, operator, previousValue, completed.get(),
400-
iter);
398+
if (ret instanceof CompletedStage<Object> completed) {
399+
return completed.isFailure() ? completed
400+
: processConditionValue(context, operator, previousValue, completed.get(),
401+
iter);
401402
} else {
402403
return ret.thenCompose(r -> processConditionValue(context, operator, previousValue, r, iter));
403404
}

independent-projects/qute/core/src/main/java/io/quarkus/qute/Results.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public final class Results {
1919

2020
public static final CompletedStage<Object> FALSE = CompletedStage.of(false);
2121
public static final CompletedStage<Object> TRUE = CompletedStage.of(true);
22+
@SuppressWarnings("unchecked")
2223
public static final CompletedStage<Object> NULL = CompletedStage.NULL;
2324

2425
private Results() {

independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionNode.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,14 @@ public CompletionStage<ResultNode> resolve(ResolutionContext context, Map<String
5353
return r;
5454
});
5555
}
56-
return helper.resolve(new SectionResolutionContextImpl(context, params, engine));
56+
try {
57+
return helper.resolve(new SectionResolutionContextImpl(context, params, engine));
58+
} catch (Throwable t) {
59+
if (t instanceof TemplateException te) {
60+
return CompletedStage.failure(te);
61+
}
62+
return CompletedStage.failure(new TemplateException(t));
63+
}
5764
}
5865

5966
@Override
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.quarkus.qute;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.util.concurrent.CompletionStage;
8+
9+
import org.junit.jupiter.api.Test;
10+
11+
public class ErrorPropagationTest {
12+
13+
@Test
14+
public void testErrorPropagation() {
15+
Engine engine = Engine.builder()
16+
.strictRendering(true)
17+
.addSectionHelper("foo", new FooSectionHelper.FooSectionHelperFactory())
18+
.addDefaults()
19+
.build();
20+
21+
TemplateException e = assertThrows(TemplateException.class,
22+
() -> engine.parse("{#let unused='var'}{bazbaz.size}{/let}").data("bazbaz", null).render());
23+
assertEquals("Rendering error: Property \"size\" not found on the base object \"null\" in expression {bazbaz.size}",
24+
e.getMessage());
25+
e = assertThrows(TemplateException.class,
26+
() -> engine.parse("{#for i in 10}{bazbaz.size}{/for}").data("bazbaz", null).render());
27+
assertEquals("Rendering error: Property \"size\" not found on the base object \"null\" in expression {bazbaz.size}",
28+
e.getMessage());
29+
e = assertThrows(TemplateException.class,
30+
() -> engine.parse("{#let unused='var'}{#if true}{bazbaz.size}{/if}{/let}").data("bazbaz", null).render());
31+
assertEquals("Rendering error: Property \"size\" not found on the base object \"null\" in expression {bazbaz.size}",
32+
e.getMessage());
33+
34+
e = assertThrows(TemplateException.class,
35+
() -> engine.parse("{#let unused='var'}{#foo /}{/let}").render());
36+
assertTrue(e.getCause() instanceof NullPointerException);
37+
e = assertThrows(TemplateException.class,
38+
() -> engine.parse("{#for i in 10}{#foo /}{/for}").render());
39+
assertTrue(e.getCause() instanceof NullPointerException);
40+
e = assertThrows(TemplateException.class,
41+
() -> engine.parse("{#if true}{#foo /}{/if}").render());
42+
assertTrue(e.getCause() instanceof NullPointerException);
43+
e = assertThrows(TemplateException.class,
44+
() -> engine.parse("{#for i in 10}{#let unused='var'}{#if true}{#foo /}{/if}{/let}{/for}").render());
45+
assertTrue(e.getCause() instanceof NullPointerException);
46+
e = assertThrows(TemplateException.class,
47+
() -> engine.parse("{#let unused='var'}{#eval '{#if true}{#foo /}{/if}' /}{/let}").render());
48+
assertTrue(e.getCause() instanceof NullPointerException);
49+
}
50+
51+
public static class FooSectionHelper implements SectionHelper {
52+
53+
@Override
54+
public CompletionStage<ResultNode> resolve(SectionResolutionContext context) {
55+
throw new NullPointerException();
56+
}
57+
58+
public static class FooSectionHelperFactory implements SectionHelperFactory<FooSectionHelper> {
59+
60+
@Override
61+
public FooSectionHelper initialize(SectionInitContext context) {
62+
return new FooSectionHelper();
63+
}
64+
65+
}
66+
67+
}
68+
69+
}

independent-projects/qute/core/src/test/java/io/quarkus/qute/LetTimeoutTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void withDataFactoryMethod() {
2727

2828
assertThatThrownBy(instance::render)
2929
.isInstanceOf(TemplateException.class)
30-
.hasRootCauseMessage("Rendering error: Key \"c\" not found in the map with keys [a] in expression {c}");
30+
.hasMessage("Rendering error: Key \"c\" not found in the map with keys [a] in expression {c}");
3131
}
3232

3333
@Test
@@ -42,7 +42,7 @@ void withInstanceThenDataForEachEntry() {
4242
}
4343
assertThatThrownBy(instance::render)
4444
.isInstanceOf(TemplateException.class)
45-
.hasRootCauseMessage(
45+
.hasMessage(
4646
"Rendering error: Key \"c\" not found in the template data map with keys [a] in expression {c}");
4747
}
4848

@@ -58,7 +58,7 @@ void withSet_withInstanceThenDataForEachEntry() {
5858
}
5959
assertThatThrownBy(instance::render)
6060
.isInstanceOf(TemplateException.class)
61-
.hasRootCauseMessage(
61+
.hasMessage(
6262
"Rendering error: Key \"c\" not found in the template data map with keys [a] in expression {c}");
6363
}
6464

@@ -73,7 +73,7 @@ void withLetWithoutEndTagwithInstanceThenDataForEachEntry() {
7373
}
7474
assertThatThrownBy(instance::render)
7575
.isInstanceOf(TemplateException.class)
76-
.hasRootCauseMessage(
76+
.hasMessage(
7777
"Rendering error: Key \"c\" not found in the template data map with keys [a] in expression {c}");
7878
}
7979
}

0 commit comments

Comments
 (0)