diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index ae0c4a11281ba..1b642f5e56063 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -84,6 +84,7 @@ import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem; import io.quarkus.gizmo.ClassOutput; import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem; +import io.quarkus.qute.CheckedTemplate; import io.quarkus.qute.Engine; import io.quarkus.qute.EngineBuilder; import io.quarkus.qute.Expression; @@ -104,7 +105,6 @@ import io.quarkus.qute.UserTagSectionHelper; import io.quarkus.qute.Variant; import io.quarkus.qute.WhenSectionHelper; -import io.quarkus.qute.api.CheckedTemplate; import io.quarkus.qute.api.ResourcePath; import io.quarkus.qute.deployment.TemplatesAnalysisBuildItem.TemplateAnalysis; import io.quarkus.qute.deployment.TypeCheckExcludeBuildItem.TypeCheck; @@ -212,10 +212,11 @@ AdditionalBeanBuildItem additionalBeans() { } @BuildStep - List collectTemplateTypeInfo(BeanArchiveIndexBuildItem index, + List collectCheckedTemplates(BeanArchiveIndexBuildItem index, BuildProducer transformers, List templatePaths, - List templateAdaptorBuildItems) { + List templateAdaptorBuildItems, + QuteConfig config) { List ret = new ArrayList<>(); Map adaptors = new HashMap<>(); @@ -246,6 +247,20 @@ List collectTemplateTypeInfo(BeanArchiveIndexBuildItem checkedTemplateAnnotations.addAll(index.getIndex().getAnnotations(Names.CHECKED_TEMPLATE_OLD)); checkedTemplateAnnotations.addAll(index.getIndex().getAnnotations(Names.CHECKED_TEMPLATE)); + // Build a set of file paths for validation + Set filePaths = new HashSet(); + for (TemplatePathBuildItem templatePath : templatePaths) { + String path = templatePath.getPath(); + filePaths.add(path); + // Also add version without suffix from the path + // For example for "items.html" also add "items" + for (String suffix : config.suffixes) { + if (path.endsWith(suffix)) { + filePaths.add(path.substring(0, path.length() - (suffix.length() + 1))); + } + } + } + for (AnnotationInstance annotation : checkedTemplateAnnotations) { if (annotation.target().kind() != Kind.CLASS) { continue; @@ -293,7 +308,23 @@ List collectTemplateTypeInfo(BeanArchiveIndexBuildItem templatePath, methodInfo.declaringClass().name(), methodInfo, checkedTemplateMethod.declaringClass().name(), checkedTemplateMethod)); } - checkTemplatePath(templatePath, templatePaths, classInfo, methodInfo); + if (!filePaths.contains(templatePath)) { + List startsWith = new ArrayList<>(); + for (String filePath : filePaths) { + if (filePath.startsWith(templatePath)) { + startsWith.add(filePath); + } + } + if (startsWith.isEmpty()) { + throw new TemplateException( + "No template matching the path " + templatePath + " could be found for: " + + classInfo.name() + "." + methodInfo.name()); + } else { + throw new TemplateException( + startsWith + " match the path " + templatePath + + " but the file suffix is not configured via the quarkus.qute.suffixes property"); + } + } Map bindings = new HashMap<>(); List parameters = methodInfo.parameters(); @@ -318,26 +349,6 @@ List collectTemplateTypeInfo(BeanArchiveIndexBuildItem return ret; } - private void checkTemplatePath(String templatePath, List templatePaths, ClassInfo enclosingClass, - MethodInfo methodInfo) { - for (TemplatePathBuildItem templatePathBuildItem : templatePaths) { - // perfect match - if (templatePathBuildItem.getPath().equals(templatePath)) { - return; - } - // if our templatePath is "Foo/hello", make it match "Foo/hello.txt" - // if they're not equal and they start with our path, there must be something left after - if (templatePathBuildItem.getPath().startsWith(templatePath) - // check that we have an extension, let variant matching work later - && templatePathBuildItem.getPath().charAt(templatePath.length()) == '.') { - return; - } - } - throw new TemplateException( - "Declared template " + templatePath + " could not be found. Either add it or delete its declaration in " - + enclosingClass.name().toString('.') + "." + methodInfo.name()); - } - @BuildStep TemplatesAnalysisBuildItem analyzeTemplates(List templatePaths, List checkedTemplates, List messageBundleMethods) { @@ -1054,7 +1065,8 @@ void validateTemplateInjectionPoints(QuteConfig config, List path.endsWith(name))) { + // For "@Location("foo/bar/baz.txt") Template baz" we try to match "foo/bar/baz.txt" + if (!filePaths.contains(name)) { validationErrors.produce(new ValidationErrorBuildItem( new TemplateException("No template found for " + injectionPoint.getTargetInfo()))); } diff --git a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java b/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java index 99e063bafd447..f6cf3a3dbbaeb 100644 --- a/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java +++ b/extensions/resteasy-qute/deployment/src/test/java/io/quarkus/qute/resteasy/deployment/MissingTemplateTest.java @@ -1,11 +1,13 @@ package io.quarkus.qute.resteasy.deployment; +import static org.junit.jupiter.api.Assertions.fail; + import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.wildfly.common.Assert; +import io.quarkus.qute.TemplateException; import io.quarkus.test.QuarkusUnitTest; public class MissingTemplateTest { @@ -15,13 +17,10 @@ public class MissingTemplateTest { .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .addClass(MissingTemplateResource.class) .addAsResource("templates/MissingTemplateResource/hello.txt")) - .assertException(t -> { - t.printStackTrace(); - Assert.assertTrue(t.getMessage().contains( - "Declared template MissingTemplateResource/missingTemplate could not be found. Either add it or delete its declaration in io.quarkus.qute.resteasy.deployment.MissingTemplateResource$Templates.missingTemplate")); - }); + .setExpectedException(TemplateException.class); @Test public void emptyTest() { + fail(); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-qute/deployment/src/test/java/io/quarkus/resteasy/reactive/qute/deployment/MissingTemplateTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-qute/deployment/src/test/java/io/quarkus/resteasy/reactive/qute/deployment/MissingTemplateTest.java index d9f8ad56ebd25..c17fda7ce455b 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-qute/deployment/src/test/java/io/quarkus/resteasy/reactive/qute/deployment/MissingTemplateTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-qute/deployment/src/test/java/io/quarkus/resteasy/reactive/qute/deployment/MissingTemplateTest.java @@ -1,11 +1,13 @@ package io.quarkus.resteasy.reactive.qute.deployment; +import static org.junit.jupiter.api.Assertions.fail; + import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.wildfly.common.Assert; +import io.quarkus.qute.TemplateException; import io.quarkus.test.QuarkusUnitTest; public class MissingTemplateTest { @@ -15,13 +17,10 @@ public class MissingTemplateTest { .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .addClass(MissingTemplateResource.class) .addAsResource("templates/MissingTemplateResource/hello.txt")) - .assertException(t -> { - t.printStackTrace(); - Assert.assertTrue(t.getMessage().contains( - "Declared template MissingTemplateResource/missingTemplate could not be found. Either add it or delete its declaration in io.quarkus.resteasy.reactive.qute.deployment.MissingTemplateResource$Templates.missingTemplate")); - }); + .setExpectedException(TemplateException.class); @Test public void emptyTest() { + fail(); } }