Skip to content

Commit

Permalink
[substitutor] adding bundlebee-directory-json-key-value-pairs-content…
Browse files Browse the repository at this point in the history
… placeholder function
  • Loading branch information
rmannibucau committed Aug 28, 2023
1 parent 4e9d17b commit cfe98da
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
import javax.json.spi.JsonProvider;
import java.io.BufferedReader;
Expand All @@ -46,19 +48,24 @@
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collector;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.Comparator.comparing;
import static java.util.Locale.ROOT;
import static java.util.Optional.ofNullable;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.logging.Level.SEVERE;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.StreamSupport.stream;

@Log
@ApplicationScoped
Expand All @@ -70,6 +77,10 @@ public class SubstitutorProducer {
@BundleBee
private JsonProvider json;

@Inject
@BundleBee
private JsonBuilderFactory jsonBuilderFactory;

@Inject
private Maven maven;

Expand Down Expand Up @@ -109,6 +120,45 @@ protected String doSubstitute(final AtomicReference<Substitutor> self, final Con
if (placeholder.equals("bundlebee-kubernetes-namespace")) {
return httpKubeClient.getNamespace();
}
if (placeholder.startsWith("bundlebee-directory-json-key-value-pairs-content:")) {
final var delegate = doSubstitute(
self, config,
"bundlebee-directory-json-key-value-pairs:" + placeholder.substring("bundlebee-directory-json-key-value-pairs-content:".length())).strip();
return delegate.substring(1, delegate.length() - 1); // drop brackets
}
if (placeholder.startsWith("bundlebee-directory-json-key-value-pairs:")) { // enable easy injection of labels or more likely annotations
final var directory = placeholder.substring("bundlebee-directory-json-key-value-pairs:".length());
// we support the pattern "/my/dir" and will take all subfiles or "/my/dir/*.ext" and will filter files by a glob pattern
final int lastSep = directory.lastIndexOf("/*");
try (final var dir = lastSep < 0 ?
Files.newDirectoryStream(Path.of(directory)) :
Files.newDirectoryStream(Path.of(directory.substring(0, lastSep)), directory.substring(lastSep + 1));
final var stream = stream(Spliterators.spliteratorUnknownSize(dir.iterator(), Spliterator.DISTINCT), false)
.onClose(() -> {
try {
dir.close();
} catch (final IOException ioe) {
throw new IllegalStateException(ioe);
}
})) {
return stream
.sorted(comparing(Path::getFileName))
.collect(Collector.of(
jsonBuilderFactory::createObjectBuilder,
(builder, path) -> {
try {
builder.add(
path.getFileName().toString().replace("____", "/"),
Files.readString(path));
} catch (final IOException e) {
throw new IllegalStateException(e);
}
},
JsonObjectBuilder::addAll,
JsonObjectBuilder::build))
.toString();
}
}
if (placeholder.startsWith("bundlebee-inline-file:")) {
final var bytes = readResource(placeholder, "bundlebee-inline-file:");
return bytes == null ? null : new String(bytes, StandardCharsets.UTF_8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.logging.Handler;
import java.util.logging.Level;
Expand All @@ -58,10 +59,15 @@ public SubstitutorProducerTest() {
}

final var producer = new SubstitutorProducer();
final var provider = JsonProvider.provider();
try {
final var json = SubstitutorProducer.class.getDeclaredField("json");
json.setAccessible(true);
json.set(producer, JsonProvider.provider());
json.set(producer, provider);

final var jsonBuilderFactory = SubstitutorProducer.class.getDeclaredField("jsonBuilderFactory");
jsonBuilderFactory.setAccessible(true);
jsonBuilderFactory.set(producer, provider.createBuilderFactory(Map.of()));

final var httpKubeClient = SubstitutorProducer.class.getDeclaredField("httpKubeClient");
httpKubeClient.setAccessible(true);
Expand Down Expand Up @@ -107,6 +113,14 @@ public <U extends OnPlaceholder> Event<U> select(final TypeLiteral<U> subtype, f
}
}

@Test
void directoryJsonKeyValuePairsContent() {
assertEquals(
"\"another/2.txt\":\"this\\nanother\\nfile = 2\\n\"," +
"\"file/1.txt\":\"this\\nis the file\\nnumber 1\\n\"",
substitutor.getOrDefault("bundlebee-directory-json-key-value-pairs-content:src/test/resources/substitutor/json/content/*.txt", "failed"));
}

@Test
void uppercase() {
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
this
another
file = 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
this
is the file
number 1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
not the right extension so ignored
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ Placeholders can use some keywords to get some particular values:
- `nowUTC`: `OffsetDateTime.now()` value with UTC `ZoneId`,
- `kubernetes.<namespace>.serviceaccount.<account name>.secrets.<secret name prefix>.data.<entry name>[.<timeout in seconds>]`: secret value looked up through Kubernetes API.
- `jsr223:<script path as file or resource or inline script>`: executes a JSR223 script to get the result, assumes that the script engine implementation is available in the JVM (not by default). To find the engine it uses the file extension or a comment in the file (for inline script) containing `bundlebee.language: <lang>`. The script contains the two functions `lookupByName` and `lookupByType` which can lookup a CDI bean to reuse BundleBee utilities (ex: `const bean = lookupByType.apply(getType('io.yupiik.bundlebee.core.kube.KubeClient'))`). If no language is found it defaults on `js`.
- `bundlebee-directory-json-key-value-pairs:/path/to/dir`: creates a JSON object from a directory. The path can be either a directory path of a directory path with a glob pattern at the end (`/path/to/dir/*.txt` for example). The keys are the filenames (simple) and `____` are converted to `/`. The values are the content of the files, filtered. Note it only works with exploded folders, not jar resources as of today.
- `bundlebee-directory-json-key-value-pairs-content:/path/to/dir`: same as `bundlebee-directory-json-key-value-pairs` but dropping enclosing brackets to be able to embed the content in an existing object (like `annotations`). Ex: `{{bundlebee-directory-json-key-value-pairs-content:resources/app/annotations/*.txt}}`.

If none of these placeholders match, then Microprofile Config is used to lookup the value (so system properties, environment variables and BundleBee args are used).

Expand Down

0 comments on commit cfe98da

Please sign in to comment.