Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce resources/site support and dev mode test #32

Merged
merged 1 commit into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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