From c51b2e73caab38017edafc1f78a42e26e386d480 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Mon, 6 Jan 2025 09:04:01 +0100 Subject: [PATCH 1/2] Fix attachment duplicate issue --- .../site/content/posts/2024-03-10-dir-post-2/beer.svg | 1 + .../site/content/posts/2024-03-10-dir-post-2/index.html | 4 ++++ .../site/content/posts/2024-03-10-dir-post/index.html | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg create mode 100644 roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html diff --git a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg new file mode 100644 index 00000000..dda44801 --- /dev/null +++ b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg @@ -0,0 +1 @@ +svg \ No newline at end of file diff --git a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html new file mode 100644 index 00000000..6134a650 --- /dev/null +++ b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html @@ -0,0 +1,4 @@ +

Hello!

+ +

{page.date()}

+beer \ No newline at end of file diff --git a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html index 6134a650..ff48f61c 100644 --- a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html +++ b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html @@ -1,3 +1,7 @@ +--- +title: 2024-03-10-dir-post +--- +

Hello!

{page.date()}

From 34a27ee4b6bcaad4e22627e5098672119ec4498b Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Mon, 6 Jan 2025 10:57:14 +0100 Subject: [PATCH 2/2] Make Roq exception more shiny --- .../io/quarkiverse/roq/it/RoqBlogTest.java | 8 ++-- .../deployment/RoqDataConverterProcessor.java | 28 +++++++++---- .../deployment/RoqDataReaderProcessor.java | 26 ++++++++---- .../deployment/converters/YamlConverter.java | 2 +- .../exception/DataConflictException.java | 13 ++++++ .../exception/DataConversionException.java | 11 +++++ .../exception/DataListBindingException.java | 13 ++++++ .../DataMappingMismatchException.java | 14 +++++++ .../exception/DataReadingException.java | 10 +++++ .../exception/DataScanningException.java | 10 +++++ .../items/DataMappingBuildItem.java | 35 +++++++++++----- .../deployment/items/RoqDataBuildItem.java | 29 ++++++++----- ...ataBindingEnforceBeanDataFileSideTest.java | 2 +- ...BindingEnforceBeanDataMappingSideTest.java | 5 ++- .../deployment/RoqFrontMatterProcessor.java | 23 ++++++++-- .../data/RoqFrontMatterDataProcessor.java | 7 ++-- .../RoqFrontMatterReadingException.java | 13 ++++++ .../exception/RoqLayoutNotFoundException.java | 13 ++++++ .../exception/RoqPathConflictException.java | 13 ++++++ .../RoqSiteIndexNotFoundException.java | 13 ++++++ .../exception/RoqSiteScanningException.java | 10 +++++ .../RoqThemeConfigurationException.java | 13 ++++++ .../record/RoqFrontMatterInitProcessor.java | 19 +++++---- .../scan/RoqFrontMatterScanProcessor.java | 42 ++++++++++--------- .../posts/2024-03-10-dir-post-2/beer.svg | 1 - .../posts/2024-03-10-dir-post-2/index.html | 4 -- .../posts/2024-03-10-dir-post/index.html | 4 -- .../exception/RoqAttachmentException.java | 13 ++++++ .../roq/frontmatter/runtime/model/Page.java | 5 ++- 29 files changed, 310 insertions(+), 89 deletions(-) create mode 100644 roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConflictException.java create mode 100644 roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConversionException.java create mode 100644 roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataListBindingException.java create mode 100644 roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataMappingMismatchException.java create mode 100644 roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataReadingException.java create mode 100644 roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataScanningException.java create mode 100644 roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqFrontMatterReadingException.java create mode 100644 roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqLayoutNotFoundException.java create mode 100644 roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqPathConflictException.java create mode 100644 roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteIndexNotFoundException.java create mode 100644 roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteScanningException.java create mode 100644 roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqThemeConfigurationException.java delete mode 100644 roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg delete mode 100644 roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html create mode 100644 roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/exception/RoqAttachmentException.java diff --git a/blog/src/test/java/io/quarkiverse/roq/it/RoqBlogTest.java b/blog/src/test/java/io/quarkiverse/roq/it/RoqBlogTest.java index e33c4822..e1ab951d 100644 --- a/blog/src/test/java/io/quarkiverse/roq/it/RoqBlogTest.java +++ b/blog/src/test/java/io/quarkiverse/roq/it/RoqBlogTest.java @@ -15,7 +15,7 @@ public void testIndex() { given().when().get("/").then().statusCode(200).body(containsString( "A Static Site Generator to easily create a static website or blog using Quarkus super-powers.")) .body(containsString("Hello, world! I'm Roq")).body(containsString("minute(s) read")) - .body(containsString("Page 1 of")).body(containsString("2024 © ROQ")); + .body(containsString("Page 1 of")).body(containsString("© ROQ")); } @Test @@ -29,7 +29,7 @@ public void testPosts() { "A Static Site Generator to easily create a static website or blog using Quarkus super-powers.")) .body(containsString("

Hello folks,

")) .body(containsString("

Welcome to Roq!

")) - .body(containsString("2024 © ROQ")); + .body(containsString("© ROQ")); } @Test @@ -37,7 +37,7 @@ public void testPostsAsciidoc() { ValidatableResponse body = given().when().get("/posts/write-your-blog-posts-in-asciidoc").then().statusCode(200).body(containsString( "Writing content is AsciiDoc format is an absolut no brainer")) .body(containsString("
quarkus extension add io.quarkiverse.roq:quarkus-roq-plugin-asciidoc
")) - .body(containsString("2024 © ROQ")); + .body(containsString("© ROQ")); System.out.println(body.extract().body().asString()); } @@ -46,7 +46,7 @@ public void testPage() { given().when().get("/events").then().statusCode(200).body(containsString( "A Static Site Generator to easily create a static website or blog using Quarkus super-powers.")) .body(containsString("

Roq 1.0 Beta

")) - .body(containsString("2024 © ROQ")); + .body(containsString("© ROQ")); } @Test diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataConverterProcessor.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataConverterProcessor.java index e8bd56ed..bac21ca1 100644 --- a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataConverterProcessor.java +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataConverterProcessor.java @@ -1,8 +1,12 @@ package io.quarkiverse.roq.data.deployment; +import java.io.IOException; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.List; +import io.quarkiverse.roq.data.deployment.exception.DataListBindingException; +import io.quarkiverse.roq.data.deployment.exception.DataReadingException; import io.quarkiverse.roq.data.deployment.items.DataMappingBuildItem; import io.quarkiverse.roq.data.deployment.items.RoqDataBeanBuildItem; import io.quarkus.deployment.annotations.BuildProducer; @@ -21,15 +25,17 @@ void convertDataMapping(List mappings, parentClass = Class.forName(mapping.getParentType().toString(), false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { - throw new RuntimeException("Class %s not found".formatted(mapping.getParentType().toString()), e); + throw new IllegalStateException("Class %s not found".formatted(mapping.getParentType().toString()), e); } final Constructor constructor; try { constructor = parentClass.getConstructor(List.class); } catch (NoSuchMethodException e) { - throw new RuntimeException( - "@DataMapping for list should declare a constructor with a List as unique parameter.", e); + throw new DataListBindingException( + "@DataMapping for list in %s should declare a constructor with a List<%s> as unique parameter" + .formatted(parentClass.getName(), mapping.getClassName()), + e); } try { @@ -38,8 +44,12 @@ void convertDataMapping(List mappings, final List list = mapping.getConverter().convertToTypedList(mapping.getContent(), itemClass); final Object data = constructor.newInstance(list); beans.produce(new RoqDataBeanBuildItem(mapping.getName(), parentClass, data, mapping.isRecord())); - } catch (Exception e) { - throw new RuntimeException("Unable to convert data to a List<%s>.".formatted(mapping.getClassName()), e); + } catch (IOException e) { + throw new DataReadingException("Unable to read data in file %s as a List<%s>" + .formatted(mapping.sourceFile(), mapping.getClassName()), e); + } catch (ClassNotFoundException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); } } else { Class beanClass; @@ -47,13 +57,15 @@ void convertDataMapping(List mappings, beanClass = Class.forName(mapping.getClassName().toString(), false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { - throw new RuntimeException("Class %s not found".formatted(mapping.getClassName().toString()), e); + throw new IllegalStateException("Class %s not found".formatted(mapping.getClassName().toString()), e); } try { final Object data = mapping.getConverter().convertToType(mapping.getContent(), beanClass); beans.produce(new RoqDataBeanBuildItem(mapping.getName(), beanClass, data, mapping.isRecord())); - } catch (Exception e) { - throw new RuntimeException("Unable to convert data to a %s.".formatted(mapping.getClassName()), e); + } catch (IOException e) { + throw new DataReadingException( + "Unable to convert data in file %s as a %s".formatted(mapping.sourceFile(), mapping.getClassName()), + e); } } } diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataReaderProcessor.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataReaderProcessor.java index 6248eeca..38b0d69f 100644 --- a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataReaderProcessor.java +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/RoqDataReaderProcessor.java @@ -14,6 +14,10 @@ import org.jboss.logging.Logger; import io.quarkiverse.roq.data.deployment.converters.DataConverterFinder; +import io.quarkiverse.roq.data.deployment.exception.DataConflictException; +import io.quarkiverse.roq.data.deployment.exception.DataConversionException; +import io.quarkiverse.roq.data.deployment.exception.DataMappingMismatchException; +import io.quarkiverse.roq.data.deployment.exception.DataScanningException; import io.quarkiverse.roq.data.deployment.items.DataMappingBuildItem; import io.quarkiverse.roq.data.deployment.items.RoqDataBuildItem; import io.quarkiverse.roq.data.deployment.items.RoqDataJsonBuildItem; @@ -49,7 +53,7 @@ void scanDataFiles(RoqProjectBuildItem roqProject, } } catch (IOException e) { - throw new RuntimeException(e); + throw new DataScanningException("Unable to scan data files", e); } } @@ -78,8 +82,8 @@ void scanDataMappings( if (config.enforceBean()) { List dataMappingErrors = collectDataMappingErrors(annotationMap.keySet(), dataJsonMap.keySet()); if (!dataMappingErrors.isEmpty()) { - throw new RuntimeException( - "Roq data is configured to enforce beans for data. Some data mapping and data files are not matching: %n%s" + throw new DataMappingMismatchException( + "Some data mappings and data files do not match: %n%s. Data mapping enforcement may be disabled in Roq." .formatted(String.join(System.lineSeparator(), dataMappingErrors))); } } @@ -107,6 +111,7 @@ void scanDataMappings( final DotName type = methodInfo.parameterType(0).asParameterizedType().arguments().get(0).name(); dataMappingProducer.produce(new DataMappingBuildItem( name, + item.sourceFile(), className, type, // need to get dynamically item.getContent(), @@ -116,6 +121,8 @@ void scanDataMappings( final DataMappingBuildItem roqMapping = new DataMappingBuildItem( name, + item.sourceFile(), + null, className, item.getContent(), @@ -126,10 +133,12 @@ void scanDataMappings( } else { // Prepare mapping as JsonObject or JsonArray (we convert here to avoid one more step) try { + final Object converted = roqDataBuildItem.converter().convert(roqDataBuildItem.getContent()); dataJsonProducer.produce(new RoqDataJsonBuildItem(name, - roqDataBuildItem.converter().convert(roqDataBuildItem.getContent()))); + converted)); } catch (IOException e) { - throw new RuntimeException(e); + throw new DataConversionException( + "Unable to convert data file %s as an Object".formatted(roqDataBuildItem.sourceFile()), e); } } } @@ -174,7 +183,8 @@ public Collection scanDataFiles(RoqProjectBuildItem roqProject (p, a) -> Files.isRegularFile(p) && isExtensionSupported(p))) { pathStream.forEach(addRoqDataBuildItem(converter, watchedFilesProducer, path, items)); } catch (IOException e) { - throw new RuntimeException("Error while scanning data files on location %s".formatted(path.toString()), e); + throw new DataScanningException( + "Error while scanning data files on location: '%s'".formatted(path.toString()), e); } } }; @@ -191,7 +201,7 @@ private static Consumer addRoqDataBuildItem( return file -> { var name = rootDir.relativize(file).toString().replaceAll("\\..*", "").replaceAll("/", "_"); if (items.containsKey(name)) { - throw new RuntimeException("Multiple data files found for name: " + name); + throw new DataConflictException("Multiple data files found for the name: '%s'.".formatted(name)); } String filename = file.getFileName().toString(); if (Path.of("").getFileSystem().equals(file.getFileSystem())) { @@ -202,7 +212,7 @@ private static Consumer addRoqDataBuildItem( if (dataConverter != null) { try { - items.put(name, new RoqDataBuildItem(name, Files.readAllBytes(file), dataConverter)); + items.put(name, new RoqDataBuildItem(name, file, Files.readAllBytes(file), dataConverter)); } catch (IOException e) { throw new UncheckedIOException("Error while decoding using %s converter: %s " .formatted(filename, converter.getClass()), e); diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/converters/YamlConverter.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/converters/YamlConverter.java index 9b2445df..9f92ec99 100644 --- a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/converters/YamlConverter.java +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/converters/YamlConverter.java @@ -30,7 +30,7 @@ public Object convert(byte[] content) throws IOException { } else if (rootNode.isArray()) { return new JsonArray(mapper.convertValue(rootNode, List.class)); } else { - throw new IOException("Unsupported YAML root element type: " + rootNode.getNodeType()); + throw new IllegalStateException("Unsupported YAML root element type: " + rootNode.getNodeType()); } } diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConflictException.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConflictException.java new file mode 100644 index 00000000..f7b99607 --- /dev/null +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConflictException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.data.deployment.exception; + +public class DataConflictException extends RuntimeException { + + public DataConflictException(String message) { + super(message); + } + + public DataConflictException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConversionException.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConversionException.java new file mode 100644 index 00000000..8e66aa6f --- /dev/null +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataConversionException.java @@ -0,0 +1,11 @@ +package io.quarkiverse.roq.data.deployment.exception; + +import java.io.IOException; +import java.io.UncheckedIOException; + +public class DataConversionException extends UncheckedIOException { + + public DataConversionException(String message, IOException cause) { + super(message, cause); + } +} diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataListBindingException.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataListBindingException.java new file mode 100644 index 00000000..9d0b1a95 --- /dev/null +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataListBindingException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.data.deployment.exception; + +public class DataListBindingException extends RuntimeException { + + public DataListBindingException(String message) { + super(message); + } + + public DataListBindingException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataMappingMismatchException.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataMappingMismatchException.java new file mode 100644 index 00000000..ba74fbc7 --- /dev/null +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataMappingMismatchException.java @@ -0,0 +1,14 @@ + +package io.quarkiverse.roq.data.deployment.exception; + +public class DataMappingMismatchException extends RuntimeException { + + public DataMappingMismatchException(String message) { + super(message); + } + + public DataMappingMismatchException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataReadingException.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataReadingException.java new file mode 100644 index 00000000..6c560a31 --- /dev/null +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataReadingException.java @@ -0,0 +1,10 @@ +package io.quarkiverse.roq.data.deployment.exception; + +import java.io.IOException; +import java.io.UncheckedIOException; + +public class DataReadingException extends UncheckedIOException { + public DataReadingException(String message, IOException cause) { + super(message, cause); + } +} diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataScanningException.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataScanningException.java new file mode 100644 index 00000000..4af11b61 --- /dev/null +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/exception/DataScanningException.java @@ -0,0 +1,10 @@ +package io.quarkiverse.roq.data.deployment.exception; + +import java.io.IOException; +import java.io.UncheckedIOException; + +public class DataScanningException extends UncheckedIOException { + public DataScanningException(String message, IOException cause) { + super(message, cause); + } +} diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/DataMappingBuildItem.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/DataMappingBuildItem.java index 127618dc..27d61d7a 100644 --- a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/DataMappingBuildItem.java +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/DataMappingBuildItem.java @@ -1,5 +1,6 @@ package io.quarkiverse.roq.data.deployment.items; +import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; @@ -15,6 +16,11 @@ public final class DataMappingBuildItem extends MultiBuildItem { */ private final String name; + /** + * Represent the source file. + */ + private final Path sourceFile; + /** * Represents the {@link DotName} of parent type. */ @@ -40,9 +46,11 @@ public final class DataMappingBuildItem extends MultiBuildItem { */ private final boolean isRecord; - public DataMappingBuildItem(String name, DotName parentType, DotName className, byte[] content, DataConverter converter, + public DataMappingBuildItem(String name, Path sourceFile, DotName parentType, DotName className, byte[] content, + DataConverter converter, boolean isRecord) { this.name = name; + this.sourceFile = sourceFile; this.parentType = parentType; this.className = className; this.content = content; @@ -50,6 +58,14 @@ public DataMappingBuildItem(String name, DotName parentType, DotName className, this.isRecord = isRecord; } + public Path sourceFile() { + return sourceFile; + } + + public String name() { + return name; + } + public String getName() { return name; } @@ -79,20 +95,17 @@ public byte[] getContent() { } @Override - public boolean equals(Object object) { - if (this == object) - return true; - if (object == null || getClass() != object.getClass()) + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; - DataMappingBuildItem that = (DataMappingBuildItem) object; - return isRecord == that.isRecord && Objects.equals(name, that.name) && Objects.equals(className, that.className) - && Arrays.equals(content, that.content) && Objects.equals(converter, that.converter); + DataMappingBuildItem that = (DataMappingBuildItem) o; + return isRecord == that.isRecord && Objects.equals(name, that.name) && Objects.equals(sourceFile, that.sourceFile) + && Objects.equals(parentType, that.parentType) && Objects.equals(className, that.className) + && Objects.deepEquals(content, that.content) && Objects.equals(converter, that.converter); } @Override public int hashCode() { - int result = Objects.hash(name, className, converter, isRecord); - result = 31 * result + Arrays.hashCode(content); - return result; + return Objects.hash(name, sourceFile, parentType, className, Arrays.hashCode(content), converter, isRecord); } } diff --git a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/RoqDataBuildItem.java b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/RoqDataBuildItem.java index 37670322..f07ac737 100644 --- a/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/RoqDataBuildItem.java +++ b/roq-data/deployment/src/main/java/io/quarkiverse/roq/data/deployment/items/RoqDataBuildItem.java @@ -1,6 +1,7 @@ package io.quarkiverse.roq.data.deployment.items; import java.io.IOException; +import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; @@ -17,6 +18,11 @@ public final class RoqDataBuildItem extends MultiBuildItem { */ private final String name; + /** + * Represent the source file. + */ + private final Path sourceFile; + /** * The content of the Roq data file. */ @@ -27,12 +33,17 @@ public final class RoqDataBuildItem extends MultiBuildItem { */ private final DataConverter converter; - public RoqDataBuildItem(String name, byte[] content, DataConverter converter) { + public RoqDataBuildItem(String name, Path sourceFile, byte[] content, DataConverter converter) { this.name = name; + this.sourceFile = sourceFile; this.content = content; this.converter = converter; } + public Path sourceFile() { + return sourceFile; + } + public String getName() { return name; } @@ -50,20 +61,16 @@ public DataConverter converter() { } @Override - public boolean equals(Object object) { - if (this == object) - return true; - if (object == null || getClass() != object.getClass()) + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; - RoqDataBuildItem that = (RoqDataBuildItem) object; - return Objects.equals(name, that.name) && Arrays.equals(content, that.content) - && Objects.equals(converter, that.converter); + RoqDataBuildItem that = (RoqDataBuildItem) o; + return Objects.equals(name, that.name) && Objects.equals(sourceFile, that.sourceFile) + && Objects.deepEquals(content, that.content) && Objects.equals(converter, that.converter); } @Override public int hashCode() { - int result = Objects.hash(name, converter); - result = 31 * result + Arrays.hashCode(content); - return result; + return Objects.hash(name, sourceFile, Arrays.hashCode(content), converter); } } diff --git a/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataFileSideTest.java b/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataFileSideTest.java index 669aaffd..4710a265 100644 --- a/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataFileSideTest.java +++ b/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataFileSideTest.java @@ -19,7 +19,7 @@ public class RoqDataBindingEnforceBeanDataFileSideTest { .assertException(e -> { assertThat(e).isInstanceOf(RuntimeException.class) .hasMessageContaining( - "Roq data is configured to enforce beans for data. Some data mapping and data files are not matching:") + "Some data mappings and data files do not match:") .hasMessageContaining("The data file 'bar' does not match with any @DataMapping class"); }); diff --git a/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataMappingSideTest.java b/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataMappingSideTest.java index 2428c966..fd91e69d 100644 --- a/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataMappingSideTest.java +++ b/roq-data/deployment/src/test/java/io/quarkiverse/roq/data/test/RoqDataBindingEnforceBeanDataMappingSideTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkiverse.roq.data.deployment.exception.DataMappingMismatchException; import io.quarkiverse.roq.data.test.util.Bar; import io.quarkus.test.QuarkusUnitTest; @@ -19,9 +20,9 @@ public class RoqDataBindingEnforceBeanDataMappingSideTest { .add(new StringAsset("quarkus.roq.dir=src/test/site\nquarkus.roq.data.enforce-bean=true"), "application.properties")) .assertException(e -> { - assertThat(e).isInstanceOf(RuntimeException.class) + assertThat(e).isInstanceOf(DataMappingMismatchException.class) .hasMessageContaining( - "Roq data is configured to enforce beans for data. Some data mapping and data files are not matching:") + "Some data mappings and data files do not match:") .hasMessageContaining("The @DataMapping#value('why') does not match with any data file"); }); diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/RoqFrontMatterProcessor.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/RoqFrontMatterProcessor.java index 8ecf66df..669099b1 100644 --- a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/RoqFrontMatterProcessor.java +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/RoqFrontMatterProcessor.java @@ -2,12 +2,11 @@ import static io.quarkiverse.roq.util.PathUtils.*; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import org.jboss.logging.Logger; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqPathConflictException; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterRawTemplateBuildItem; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterStaticFileBuildItem; import io.quarkiverse.roq.frontmatter.runtime.RoqFrontMatterMessages; @@ -20,8 +19,10 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.qute.deployment.TemplatePathBuildItem; import io.quarkus.qute.deployment.ValidationParserHookBuildItem; +import io.quarkus.runtime.LaunchMode; import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem; import io.quarkus.vertx.http.deployment.spi.GeneratedStaticResourceBuildItem; @@ -124,11 +125,27 @@ void bindEndpoints( @BuildStep void bindStaticFiles( RoqSiteConfig config, + LaunchModeBuildItem launchMode, BuildProducer selectedPathProducer, List staticFiles, BuildProducer staticResourcesProducer) { + Map paths = new HashMap<>(); for (RoqFrontMatterStaticFileBuildItem staticFile : staticFiles) { final String endpoint = prefixWithSlash(staticFile.link()); + final String prev = paths.put(endpoint, staticFile.filePath().toString()); + if (prev != null) { + if (launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT) { + LOGGER.warnf( + "Conflict detected: Duplicate path (%s) found in %s and %s. In development, the first occurrence will be kept, but this will cause an exception in normal mode.", + endpoint, prev, staticFile.filePath()); + continue; + } else { + throw new RoqPathConflictException( + "Conflict detected: Duplicate path (%s) found in %s and %s".formatted(endpoint, prev, + staticFile.filePath())); + } + } + LOGGER.debugf("Published static file: '%s'", endpoint); selectedPathProducer.produce(new SelectedPathBuildItem(endpoint, null)); staticResourcesProducer.produce(new GeneratedStaticResourceBuildItem( diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/data/RoqFrontMatterDataProcessor.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/data/RoqFrontMatterDataProcessor.java index d33eb92c..0c5b5fbf 100644 --- a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/data/RoqFrontMatterDataProcessor.java +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/data/RoqFrontMatterDataProcessor.java @@ -8,6 +8,7 @@ import io.quarkiverse.roq.frontmatter.deployment.RoqFrontMatterRootUrlBuildItem; import io.quarkiverse.roq.frontmatter.deployment.TemplateLink; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqLayoutNotFoundException; import io.quarkiverse.roq.frontmatter.deployment.publish.RoqFrontMatterPublishPageBuildItem; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterRawTemplateBuildItem; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterRawTemplateBuildItem.Attachment; @@ -100,9 +101,9 @@ public static JsonObject mergeParents(RoqSiteConfig config, RoqFrontMatterRawTem while (parent != null) { if (!byId.containsKey(parent)) { final String layoutKey = RoqFrontMatterScanProcessor.getLayoutKey(config.theme(), parent); - throw new RuntimeException( - "Layout '%s' not found for file: '%s'. Available layouts: %s.".formatted(layoutKey, - item.info().sourceFileName(), getAvailableLayouts(config, byId))); + throw new RoqLayoutNotFoundException( + "Layout '%s' not found for file '%s'. Available layouts are: %s." + .formatted(layoutKey, item.info().sourceFileName(), getAvailableLayouts(config, byId))); } final RoqFrontMatterRawTemplateBuildItem parentItem = byId.get(parent); parent = parentItem.layout(); diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqFrontMatterReadingException.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqFrontMatterReadingException.java new file mode 100644 index 00000000..1e9b6ffb --- /dev/null +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqFrontMatterReadingException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.frontmatter.deployment.exception; + +public class RoqFrontMatterReadingException extends RuntimeException { + + public RoqFrontMatterReadingException(String message) { + super(message); + } + + public RoqFrontMatterReadingException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqLayoutNotFoundException.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqLayoutNotFoundException.java new file mode 100644 index 00000000..fde26cbf --- /dev/null +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqLayoutNotFoundException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.frontmatter.deployment.exception; + +public class RoqLayoutNotFoundException extends RuntimeException { + + public RoqLayoutNotFoundException(String message) { + super(message); + } + + public RoqLayoutNotFoundException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqPathConflictException.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqPathConflictException.java new file mode 100644 index 00000000..04b255d4 --- /dev/null +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqPathConflictException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.frontmatter.deployment.exception; + +public class RoqPathConflictException extends RuntimeException { + + public RoqPathConflictException(String message) { + super(message); + } + + public RoqPathConflictException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteIndexNotFoundException.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteIndexNotFoundException.java new file mode 100644 index 00000000..8e6ddf38 --- /dev/null +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteIndexNotFoundException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.frontmatter.deployment.exception; + +public class RoqSiteIndexNotFoundException extends RuntimeException { + + public RoqSiteIndexNotFoundException(String message) { + super(message); + } + + public RoqSiteIndexNotFoundException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteScanningException.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteScanningException.java new file mode 100644 index 00000000..41722d53 --- /dev/null +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqSiteScanningException.java @@ -0,0 +1,10 @@ +package io.quarkiverse.roq.frontmatter.deployment.exception; + +import java.io.IOException; +import java.io.UncheckedIOException; + +public class RoqSiteScanningException extends UncheckedIOException { + public RoqSiteScanningException(String message, IOException cause) { + super(message, cause); + } +} diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqThemeConfigurationException.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqThemeConfigurationException.java new file mode 100644 index 00000000..9f0cfcfe --- /dev/null +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/exception/RoqThemeConfigurationException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.frontmatter.deployment.exception; + +public class RoqThemeConfigurationException extends RuntimeException { + + public RoqThemeConfigurationException(String message) { + super(message); + } + + public RoqThemeConfigurationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/record/RoqFrontMatterInitProcessor.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/record/RoqFrontMatterInitProcessor.java index 1de12189..f9b9bd04 100644 --- a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/record/RoqFrontMatterInitProcessor.java +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/record/RoqFrontMatterInitProcessor.java @@ -15,6 +15,8 @@ import io.quarkiverse.roq.frontmatter.deployment.RoqFrontMatterOutputBuildItem; import io.quarkiverse.roq.frontmatter.deployment.RoqFrontMatterRootUrlBuildItem; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqPathConflictException; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqSiteIndexNotFoundException; import io.quarkiverse.roq.frontmatter.deployment.publish.RoqFrontMatterPublishDerivedCollectionBuildItem; import io.quarkiverse.roq.frontmatter.deployment.publish.RoqFrontMatterPublishDocumentPageBuildItem; import io.quarkiverse.roq.frontmatter.deployment.publish.RoqFrontMatterPublishPageBuildItem; @@ -26,6 +28,7 @@ import io.quarkus.deployment.annotations.*; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.runtime.LaunchMode; import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.HandlerType; @@ -119,8 +122,8 @@ RoqFrontMatterOutputBuildItem bindSite( } // Create Site bean if (indexPageItem == null) { - throw new RuntimeException( - "Site index page (index.html,md,...) not found. A site index is required by Roq, please create one to continue..."); + throw new RoqSiteIndexNotFoundException( + "Site index page (index.html, index.md, etc.) not found. A site index is required by Roq. Please create one to continue."); } final Map>> collectionsMap = collectionItems.stream().collect( Collectors.toMap(RoqFrontMatterCollectionBuildItem::name, RoqFrontMatterCollectionBuildItem::documents)); @@ -148,12 +151,14 @@ RoqFrontMatterOutputBuildItem bindSite( final RoqFrontMatterPageBuildItem prev = itemsMap.put(i.url().path(), i); allPagesByPath.put(i.url().path(), i.page()); if (prev != null) { - if (launchMode.getLaunchMode().isDevOrTest()) { - LOGGER.warnf("The same url (%s) has been found more than once in %s and %s", i.url().path(), prev.id(), - i.id()); + if (launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT) { + LOGGER.warnf( + "Conflict detected: Duplicate path (%s) found in %s and %s. In development, the first occurrence will be kept, but this will cause an exception in normal mode.", + i.url().path(), prev.id(), i.id()); } else { - throw new RuntimeException("The same url (%s) has been found more than once in %s and %s" - .formatted(i.url().path(), prev.id(), i.id())); + throw new RoqPathConflictException( + "Conflict detected: Duplicate path (%s) found in %s and %s".formatted(i.url().path(), prev.id(), + i.id())); } } } diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java index 697caeb3..e48ed79a 100644 --- a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java @@ -7,7 +7,6 @@ import static java.util.function.Predicate.not; import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -35,6 +34,9 @@ import io.quarkiverse.roq.deployment.items.RoqProjectBuildItem; import io.quarkiverse.roq.frontmatter.deployment.RoqFrontMatterProcessor; import io.quarkiverse.roq.frontmatter.deployment.data.RoqFrontMatterDataModificationBuildItem; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqFrontMatterReadingException; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqSiteScanningException; +import io.quarkiverse.roq.frontmatter.deployment.exception.RoqThemeConfigurationException; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterQuteMarkupBuildItem.QuteMarkupSection; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterRawTemplateBuildItem.Attachment; import io.quarkiverse.roq.frontmatter.deployment.scan.RoqFrontMatterRawTemplateBuildItem.TemplateType; @@ -49,7 +51,6 @@ import io.quarkus.qute.deployment.TemplatePathBuildItem; import io.quarkus.qute.deployment.TemplateRootBuildItem; import io.quarkus.qute.runtime.QuteConfig; -import io.quarkus.runtime.configuration.ConfigurationException; import io.vertx.core.http.impl.MimeMapping; import io.vertx.core.json.JsonObject; @@ -117,7 +118,7 @@ void scan(RoqProjectBuildItem roqProject, } } catch (IOException e) { - throw new RuntimeException(e); + throw new RoqSiteScanningException("Unable to scan the Roq project", e); } } @@ -225,8 +226,8 @@ private static void scanContent(YAMLMapper mapper, QuteConfig quteConfig, RoqSit } }); } catch (IOException e) { - throw new RuntimeException( - "Was not possible to scan content files on location %s".formatted(contentDir), e); + throw new RoqSiteScanningException( + "Unable to scan content files at location: %s".formatted(contentDir), e); } } @@ -243,7 +244,7 @@ private static void watchDirectory(Path dir, BuildProducer 65535) { LOGGER.warnf( - "Ignoring template '%s' because it is too large for recording, consider splitting it.", + "Template '%s' is too large for recording and will be ignored. Consider splitting it into smaller parts.", link); return; } @@ -331,12 +333,13 @@ private static void scanTemplates(QuteConfig quteConfig, .extensionInfo(RoqFrontMatterProcessor.FEATURE) .build()); } catch (IOException e) { - throw new RuntimeException("Error while reading the file %s" - .formatted(p), e); + throw new RoqSiteScanningException( + "Error while reading template file: %s".formatted(p), e); } }); } catch (IOException e) { - throw new RuntimeException("Was not possible to scan templates dir %s".formatted(templatesRoot), e); + throw new RoqSiteScanningException( + "Error while reading templates dir: %s".formatted(templatesRoot), e); } } @@ -371,7 +374,8 @@ private static Consumer addBuildItem(Path root, try { fullContent = Files.readString(file, StandardCharsets.UTF_8); } catch (IOException e) { - throw new UncheckedIOException("Error while reading file: " + sourcePath, e); + throw new RoqSiteScanningException( + "Error while reading template file: %s".formatted(sourcePath), e); } JsonObject fm; String content = fullContent; @@ -379,7 +383,8 @@ private static Consumer addBuildItem(Path root, try { fm = readFM(mapper, fullContent); } catch (JsonProcessingException | IllegalArgumentException e) { - throw new RuntimeException("FrontMatter parsing error for file: " + sourcePath, e); + throw new RoqFrontMatterReadingException( + "Error reading YAML FrontMatter block (enclosed by '---') in file: %s".formatted(sourcePath)); } content = stripFrontMatter(fullContent); } else { @@ -428,9 +433,8 @@ private static Consumer addBuildItem(Path root, .forEach(p -> attachments .add(new Attachment(resolveAttachmentLink(p, refDir), p))); } catch (IOException e) { - throw new UncheckedIOException( - "Error while scanning pages static attachments files in " + attachmentDir, - e); + throw new RoqSiteScanningException( + "Error scanning static attachment files in directory: %s".formatted(attachmentDir), e); } } } @@ -524,9 +528,9 @@ private static String normalizedLayout(Optional theme, String layout, St if (theme.isPresent()) { normalized = normalized.replace(":theme", theme.get()); } else { - throw new ConfigurationException( - "No theme detected! Using :theme in 'layout: " + layout - + " is only possible with a theme installed."); + throw new RoqThemeConfigurationException( + "No theme detected! Using ':theme' in 'layout: %s' is only possible with a theme installed as a dependency." + .formatted(layout)); } } diff --git a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg deleted file mode 100644 index dda44801..00000000 --- a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/beer.svg +++ /dev/null @@ -1 +0,0 @@ -svg \ No newline at end of file diff --git a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html deleted file mode 100644 index 6134a650..00000000 --- a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post-2/index.html +++ /dev/null @@ -1,4 +0,0 @@ -

Hello!

- -

{page.date()}

-beer \ No newline at end of file diff --git a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html index ff48f61c..6134a650 100644 --- a/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html +++ b/roq-frontmatter/deployment/src/test/resources/site/content/posts/2024-03-10-dir-post/index.html @@ -1,7 +1,3 @@ ---- -title: 2024-03-10-dir-post ---- -

Hello!

{page.date()}

diff --git a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/exception/RoqAttachmentException.java b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/exception/RoqAttachmentException.java new file mode 100644 index 00000000..b6887fdb --- /dev/null +++ b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/exception/RoqAttachmentException.java @@ -0,0 +1,13 @@ +package io.quarkiverse.roq.frontmatter.runtime.exception; + +public class RoqAttachmentException extends RuntimeException { + + public RoqAttachmentException(String message) { + super(message); + } + + public RoqAttachmentException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java index 5ab4c303..1a630320 100644 --- a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java +++ b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java @@ -5,6 +5,7 @@ import jakarta.enterprise.inject.Vetoed; +import io.quarkiverse.roq.frontmatter.runtime.exception.RoqAttachmentException; import io.quarkiverse.roq.util.PathUtils; import io.quarkus.arc.Arc; import io.quarkus.qute.TemplateData; @@ -142,13 +143,13 @@ default RoqUrl attachment(Object name) { return null; } if (!info().hasAttachments()) { - throw new RuntimeException("Can't find '%s' in '%s' which has no attachment.".formatted(name, sourcePath())); + throw new RoqAttachmentException("Can't find '%s' in '%s' which has no attachment.".formatted(name, sourcePath())); } final String clean = ((String) name).replace("./", ""); if (info().hasAttachments() && attachments().contains(clean)) { return url().resolve(clean); } else { - throw new RuntimeException("Attachment '%s' was not found for `%s` (%s)".formatted(name, sourcePath(), + throw new RoqAttachmentException("Attachment '%s' was not found for `%s` (%s)".formatted(name, sourcePath(), String.join(",", attachments()))); } }