Skip to content

Commit

Permalink
Merge pull request #32 from ia3andy/resources
Browse files Browse the repository at this point in the history
Introduce resources/site support and dev mode test
  • Loading branch information
ia3andy authored Jul 1, 2024
2 parents 2b05992 + 2894d3c commit bc1489b
Show file tree
Hide file tree
Showing 20 changed files with 276 additions and 181 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkiverse.roq.deployment;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -20,10 +21,20 @@ public class RoqProjectProcessor {
RoqProjectBuildItem findProject(RoqConfig config, OutputTargetBuildItem outputTarget,
CurateOutcomeBuildItem curateOutcome) {
final RoqProjectBuildItem.RoqProject project = resolveProjectDirs(config, curateOutcome, outputTarget);
if (project == null) {
return null;

String resourceSiteDir;
try {
final boolean hasResourceSiteDir = Thread.currentThread().getContextClassLoader()
.getResources(config.resourceSiteDir()).hasMoreElements();
resourceSiteDir = hasResourceSiteDir ? config.resourceSiteDir() : null;
} catch (IOException e) {
resourceSiteDir = null;
}
return new RoqProjectBuildItem(project);
final RoqProjectBuildItem roqProject = new RoqProjectBuildItem(project, resourceSiteDir);
if (!roqProject.isActive()) {
LOG.warn("Not Roq site directory found. It is recommended to remove the quarkus-roq extension if not used.");
}
return roqProject;
}

/**
Expand All @@ -40,7 +51,6 @@ private static RoqProjectBuildItem.RoqProject resolveProjectDirs(RoqConfig confi
OutputTargetBuildItem outputTarget) {
Path projectRoot = findProjectRoot(outputTarget.getOutputDirectory());
Path configuredSiteDirPath = Paths.get(config.siteDir().trim());

if (projectRoot == null || !Files.isDirectory(projectRoot)) {

if (configuredSiteDirPath.isAbsolute() && Files.isDirectory(configuredSiteDirPath)) {
Expand All @@ -55,9 +65,6 @@ private static RoqProjectBuildItem.RoqProject resolveProjectDirs(RoqConfig confi
final Path siteRoot = projectRoot.resolve(configuredSiteDirPath).normalize();

if (!Files.isDirectory(siteRoot)) {
LOG.warnf(
"Roq directory not found 'quarkus.roq.site-dir=%s' resolved to '%s'. It is recommended to remove the quarkus-roq extension if not used.",
config.siteDir(), siteRoot.toAbsolutePath());
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@
public interface RoqConfig {

String DEFAULT_SITE_DIR = "src/main/site";
String DEFAULT_RESOURCE_SITE_DIR = "site";

/**
* Path to the static root directory (relative to the project root).
* Path to the Roq site directory (relative to the project root).
*/
@WithDefault(DEFAULT_SITE_DIR)
String siteDir();

/**
* Path to the Roq site directory in the resources.
*/
@WithDefault(DEFAULT_RESOURCE_SITE_DIR)
String resourceSiteDir();

static boolean isEqual(RoqConfig q1, RoqConfig q2) {
if (!Objects.equals(q1.siteDir(), q2.siteDir())) {
return false;
}
if (!Objects.equals(q1.resourceSiteDir(), q2.resourceSiteDir())) {
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,34 @@
import java.nio.file.Path;
import java.util.function.Consumer;

import io.quarkiverse.roq.util.PathUtils;
import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.runtime.util.ClassPathUtils;

public final class RoqProjectBuildItem extends SimpleBuildItem {
private final RoqProject project;
private final String resourceSiteDir;

public RoqProjectBuildItem(RoqProject project) {
public RoqProjectBuildItem(RoqProject project, String resourceSiteDir) {
this.project = project;
this.resourceSiteDir = resourceSiteDir;
}

public RoqProject dirs() {
public RoqProject project() {
return project;
}

public boolean isActive() {
return project != null || resourceSiteDir != null;
}

public void consumePathFromSite(String resource, Consumer<Path> consumer) throws IOException {
// TODO: in the future we might want to scan dependencies when configured
consumer.accept(project.siteDir().resolve(resource));
if (resourceSiteDir != null) {
ClassPathUtils.consumeAsPaths(PathUtils.join(resourceSiteDir, resource), consumer);
}
if (project != null) {
consumer.accept(project.siteDir().resolve(resource));
}
}

/**
Expand Down
19 changes: 18 additions & 1 deletion docs/modules/ROOT/pages/includes/quarkus-roq.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ a|icon:lock[title=Fixed at build time] [[quarkus-roq_quarkus-roq-site-dir]]`link

[.description]
--
Path to the static root directory (relative to the project root).
Path to the Roq site directory (relative to the project root).

ifdef::add-copy-button-to-env-var[]
Environment variable: env_var_with_copy_button:+++QUARKUS_ROQ_SITE_DIR+++[]
Expand All @@ -26,4 +26,21 @@ endif::add-copy-button-to-env-var[]
--|string
|`src/main/site`


a|icon:lock[title=Fixed at build time] [[quarkus-roq_quarkus-roq-resource-site-dir]]`link:#quarkus-roq_quarkus-roq-resource-site-dir[quarkus.roq.resource-site-dir]`


[.description]
--
Path to the Roq site directory in the resources.

ifdef::add-copy-button-to-env-var[]
Environment variable: env_var_with_copy_button:+++QUARKUS_ROQ_RESOURCE_SITE_DIR+++[]
endif::add-copy-button-to-env-var[]
ifndef::add-copy-button-to-env-var[]
Environment variable: `+++QUARKUS_ROQ_RESOURCE_SITE_DIR+++`
endif::add-copy-button-to-env-var[]
--|string
|`site`

|===
22 changes: 18 additions & 4 deletions roq-data/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,32 @@
<artifactId>quarkus-roq-data</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.quarkiverse.roq.data.deployment;

public interface DataConverter {

Object convert(String content);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package io.quarkiverse.roq.data.deployment;

import java.util.function.Function;
import io.vertx.core.json.Json;

import io.vertx.core.json.JsonObject;

public class JsonConverter implements Function<String, JsonObject> {
public class JsonConverter implements DataConverter {
@Override
public JsonObject apply(String content) {
return new JsonObject(content);
public Object convert(String content) {
return Json.decodeValue(content);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package io.quarkiverse.roq.data.deployment;

import java.util.function.Function;

import io.vertx.core.json.JsonObject;

public class JsonObjectConverter {

private static final YamlConverter YAML_CONVERTER = new YamlConverter();
Expand All @@ -15,9 +11,9 @@ public enum Extensions {
YML(".yml", YAML_CONVERTER);

private final String extension;
private final Function<String, JsonObject> converter;
private final DataConverter converter;

Extensions(String extension, Function<String, JsonObject> converter) {
Extensions(String extension, DataConverter converter) {
this.extension = extension;
this.converter = converter;
}
Expand All @@ -26,8 +22,8 @@ public String getExtension() {
return extension;
}

public JsonObject convert(String content) {
return this.converter.apply(content);
public Object convert(String content) {
return this.converter.convert(content);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package io.quarkiverse.roq.data.deployment;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
Expand All @@ -13,7 +16,7 @@
import io.quarkiverse.roq.deployment.items.RoqProjectBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.vertx.core.json.JsonObject;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;

public class ReadRoqDataProcessor {

Expand All @@ -22,10 +25,11 @@ public class ReadRoqDataProcessor {
RoqDataConfig roqDataConfig;

@BuildStep
void scanDataFiles(RoqProjectBuildItem roqProject, RoqDataConfig config, BuildProducer<RoqDataJsonBuildItem> dataProducer) {
if (roqProject != null) {
void scanDataFiles(RoqProjectBuildItem roqProject, RoqDataConfig config, BuildProducer<RoqDataJsonBuildItem> dataProducer,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFilesProducer) {
if (roqProject.isActive()) {
try {
Set<RoqDataJsonBuildItem> items = scanDataFiles(roqProject, config);
Collection<RoqDataJsonBuildItem> items = scanDataFiles(roqProject, watchedFilesProducer, config);

for (RoqDataJsonBuildItem item : items) {
dataProducer.produce(item);
Expand All @@ -38,37 +42,46 @@ void scanDataFiles(RoqProjectBuildItem roqProject, RoqDataConfig config, BuildPr

}

public Set<RoqDataJsonBuildItem> scanDataFiles(RoqProjectBuildItem roqProject, RoqDataConfig config) throws IOException {
public Collection<RoqDataJsonBuildItem> scanDataFiles(RoqProjectBuildItem roqProject,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFilesProducer, RoqDataConfig config)
throws IOException {

HashSet<RoqDataJsonBuildItem> items = new HashSet<>();
Map<String, RoqDataJsonBuildItem> items = new HashMap<>();

roqProject.consumePathFromSite(config.dir(), (path) -> {
if (Files.isDirectory(path)) {
try (Stream<Path> pathStream = Files.find(path, Integer.MAX_VALUE,
(p, a) -> Files.isRegularFile(p) && isExtensionSupported(p))) {
pathStream.forEach(addRoqDataJsonBuildItem(path, items));
pathStream.forEach(addRoqDataJsonBuildItem(watchedFilesProducer, path, items));
} catch (IOException e) {
throw new RuntimeException("Error while scanning data files on location %s".formatted(path.toString()), e);
}
}
});

return items;
return items.values();
}

private static Consumer<Path> addRoqDataJsonBuildItem(Path rootDir, HashSet<RoqDataJsonBuildItem> items) {
private static Consumer<Path> addRoqDataJsonBuildItem(BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFilesProducer,
Path rootDir, Map<String, RoqDataJsonBuildItem> items) {
return file -> {
var name = rootDir.relativize(file).toString().replaceAll("\\..*", "").replaceAll("\\/", "_");
var name = rootDir.relativize(file).toString().replaceAll("\\..*", "").replaceAll("/", "_");
if (items.containsKey(name)) {
throw new RuntimeException("Multiple data files found for name: " + name);
}
String filename = file.getFileName().toString();

if (Path.of("").getFileSystem().equals(file.getFileSystem())) {
// We don't need to watch file out of the local filesystem
watchedFilesProducer.produce(new HotDeploymentWatchedFileBuildItem(file.toAbsolutePath().toString(), true));
}
JsonObjectConverter.Extensions converter = JsonObjectConverter.findExtensionConverter(filename);

if (converter != null) {
try {
JsonObject jsonObject = converter.convert(Files.readString(file, StandardCharsets.UTF_8));
items.add(new RoqDataJsonBuildItem(name, jsonObject));
Object value = converter.convert(Files.readString(file, StandardCharsets.UTF_8));
items.put(name, new RoqDataJsonBuildItem(name, value));
} catch (IOException e) {
throw new RuntimeException("Was not possible to read the file %s using the converter for %s extension"
throw new UncheckedIOException("Error while decoding using %s converter: %s "
.formatted(filename, converter.getExtension()), e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

class RoqDataProcessor {
Expand All @@ -34,12 +35,22 @@ void generateInjectable(BuildProducer<SyntheticBeanBuildItem> beansProducer,

for (RoqDataJsonBuildItem roqData : roqDataJsonBuildItems) {
LOGGER.info("Creating synthetic bean with identifier " + roqData.getName());
beansProducer.produce(SyntheticBeanBuildItem.configure(JsonObject.class)
.scope(ApplicationScoped.class)
.setRuntimeInit()
.addQualifier().annotation(Named.class).addValue("value", roqData.getName()).done()
.runtimeValue(recorder.createRoqDataJson(roqData.getJsonObject()))
.done());
if (roqData.getData() instanceof JsonObject) {
beansProducer.produce(SyntheticBeanBuildItem.configure(JsonObject.class)
.scope(ApplicationScoped.class)
.setRuntimeInit()
.addQualifier().annotation(Named.class).addValue("value", roqData.getName()).done()
.runtimeValue(recorder.createRoqDataJson(roqData.getData()))
.done());
} else if (roqData.getData() instanceof JsonArray) {
beansProducer.produce(SyntheticBeanBuildItem.configure(JsonArray.class)
.scope(ApplicationScoped.class)
.setRuntimeInit()
.addQualifier().annotation(Named.class).addValue("value", roqData.getName()).done()
.runtimeValue(recorder.createRoqDataJson(roqData.getData()))
.done());
}

}
}

Expand Down
Loading

0 comments on commit bc1489b

Please sign in to comment.