Skip to content

Commit

Permalink
Merge pull request #15412 from mkouba/issue-12355
Browse files Browse the repository at this point in the history
Qute Type-safe Templates - fix template path validation
  • Loading branch information
mkouba authored Mar 4, 2021
2 parents 27ed984 + 2c14a4f commit 77b9293
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -212,10 +212,11 @@ AdditionalBeanBuildItem additionalBeans() {
}

@BuildStep
List<CheckedTemplateBuildItem> collectTemplateTypeInfo(BeanArchiveIndexBuildItem index,
List<CheckedTemplateBuildItem> collectCheckedTemplates(BeanArchiveIndexBuildItem index,
BuildProducer<BytecodeTransformerBuildItem> transformers,
List<TemplatePathBuildItem> templatePaths,
List<CheckedTemplateAdapterBuildItem> templateAdaptorBuildItems) {
List<CheckedTemplateAdapterBuildItem> templateAdaptorBuildItems,
QuteConfig config) {
List<CheckedTemplateBuildItem> ret = new ArrayList<>();

Map<DotName, CheckedTemplateAdapter> adaptors = new HashMap<>();
Expand Down Expand Up @@ -246,6 +247,20 @@ List<CheckedTemplateBuildItem> 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<String> filePaths = new HashSet<String>();
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;
Expand Down Expand Up @@ -293,7 +308,23 @@ List<CheckedTemplateBuildItem> collectTemplateTypeInfo(BeanArchiveIndexBuildItem
templatePath, methodInfo.declaringClass().name(), methodInfo,
checkedTemplateMethod.declaringClass().name(), checkedTemplateMethod));
}
checkTemplatePath(templatePath, templatePaths, classInfo, methodInfo);
if (!filePaths.contains(templatePath)) {
List<String> 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<String, String> bindings = new HashMap<>();
List<Type> parameters = methodInfo.parameters();
Expand All @@ -318,26 +349,6 @@ List<CheckedTemplateBuildItem> collectTemplateTypeInfo(BeanArchiveIndexBuildItem
return ret;
}

private void checkTemplatePath(String templatePath, List<TemplatePathBuildItem> 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<TemplatePathBuildItem> templatePaths,
List<CheckedTemplateBuildItem> checkedTemplates, List<MessageBundleMethodBuildItem> messageBundleMethods) {
Expand Down Expand Up @@ -1054,7 +1065,8 @@ void validateTemplateInjectionPoints(QuteConfig config, List<TemplatePathBuildIt
if (name != null) {
// For "@Inject Template items" we try to match "items"
// For "@Location("github/pulls") Template pulls" we try to match "github/pulls"
if (filePaths.stream().noneMatch(path -> 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())));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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();
}
}

0 comments on commit 77b9293

Please sign in to comment.