Skip to content

Commit

Permalink
[command] add list-alveoli command plus enable to dump placeholders (…
Browse files Browse the repository at this point in the history
…per alveolus) in JSON
  • Loading branch information
rmannibucau committed Jan 8, 2024
1 parent 2b7f11b commit 0ff996d
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2021 - present - Yupiik SAS - https://www.yupiik.com
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.yupiik.bundlebee.core.command.impl;

import io.yupiik.bundlebee.core.command.Executable;
import io.yupiik.bundlebee.core.configuration.Description;
import io.yupiik.bundlebee.core.descriptor.Manifest;
import io.yupiik.bundlebee.core.qualifier.BundleBee;
import io.yupiik.bundlebee.core.service.AlveolusHandler;
import lombok.Data;
import lombok.extern.java.Log;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.json.JsonArrayBuilder;
import javax.json.JsonBuilderFactory;
import javax.json.bind.annotation.JsonbProperty;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collector;
import java.util.stream.Stream;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

@Log
@Dependent
public class ListAlveoliCommand implements Executable {
@Inject
@Description("Root dependency to download to get the manifest. If set to `auto` it is assumed to be present in current classpath.")
@ConfigProperty(name = "bundlebee.list-alveoli.from", defaultValue = "auto")
private String from;

@Inject
@Description("Manifest to load to start to find the alveolus. This optional setting mainly enables to use dependencies easily. " +
"Ignored if set to `skip`.")
@ConfigProperty(name = "bundlebee.list-alveoli.manifest", defaultValue = "skip")
private String manifest;

@Inject
@Description("`logger` means the standard bundlebee logging output stream else it is considered as a file path. " +
"Note that if you end the filename with `.json` it will be formatted as json else just readable text.")
@ConfigProperty(name = "bundlebee.list-alveoli.output", defaultValue = "logger")
private String output;

@Inject
private AlveolusHandler visitor;

@Inject
@BundleBee
private JsonBuilderFactory json;

@Override
public String name() {
return "list-alveoli";
}

@Override
public String description() {
return "Lists all found alveoli";
}

@Override
public CompletionStage<?> execute() {
return visitor.findManifest(from, manifest, null)
.thenApply(m -> m == null || m.getAlveoli() == null ?
visitor.findManifestsFromClasspath(null)
.flatMap(it -> it.getAlveoli() == null ? Stream.empty() : it.getAlveoli().stream())
.collect(toList()) :
m.getAlveoli())
.thenApply(items -> {
switch (output) {
case "logger":
log.info(asText(items));
break;
case "logger.json":
log.info(asJson(items));
break;
default:
final var out = Path.of(output);
try {
if (out.getParent() != null) {
Files.createDirectories(out.getParent());
}
Files.writeString(out, output.endsWith(".json") ?
asJson(items) :
asText(items));
} catch (final IOException ioe) {
throw new IllegalStateException(ioe);
}
}
return items;
});
}

private String asJson(final List<Manifest.Alveolus> items) {
return json.createObjectBuilder()
.add("items", items.stream()
.sorted(comparing(Manifest.Alveolus::getName))
.map(it -> json.createObjectBuilder().add("name", it.getName()).build())
.collect(Collector.of(
json::createArrayBuilder,
JsonArrayBuilder::add,
JsonArrayBuilder::addAll)))
.build()
.toString();
}

private static String asText(final List<Manifest.Alveolus> a) {
return a.isEmpty() ?
"No alveolus found." :
a.stream()
.sorted(comparing(Manifest.Alveolus::getName))
.map(i -> "- " + i.getName())
.collect(joining("\n", "Found alveoli:\n", "\n"));
}

@Data
public static class Item {
@JsonbProperty
private final String name;
}

@Data
public static class Items {
@JsonbProperty
private final List<Item> item;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ public String description() {

@Override
public CompletionStage<?> execute() {
return completedFuture(checks.stream()
final var output = checks.stream()
.map(c -> " *" + c.name())
.collect(joining("\n")));
.collect(joining("\n"));
log.info(output);
return completedFuture(output);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.json.JsonArrayBuilder;
import javax.json.JsonBuilderFactory;
import javax.json.JsonString;
import javax.json.spi.JsonProvider;
import java.io.IOException;
Expand All @@ -48,6 +49,7 @@
import java.util.stream.Collector;
import java.util.stream.Stream;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
Expand Down Expand Up @@ -95,6 +97,11 @@ public class PlaceholderExtractorCommand extends VisitorCommand {
@ConfigProperty(name = "bundlebee.placeholder-extract.propertiesFilename", defaultValue = "placeholders.properties")
private String propertiesFilename;

@Inject
@Description("JSON filename (relative to `dumpLocation`) when `outputType` is `FILE`. Ignores JSON dump if value is `skip`.")
@ConfigProperty(name = "bundlebee.placeholder-extract.jsonFilename", defaultValue = "placeholders.json")
private String jsonFilename;

@Inject
@Description("Completion properties filename - see https://github.com/rmannibucau/vscode-properties-custom-completion - (relative to `dumpLocation`) when `outputType` is `FILE`. Ignores this extraction if value is `skip`.")
@ConfigProperty(name = "bundlebee.placeholder-extract.completionFilename", defaultValue = "placeholders.completion.properties")
Expand Down Expand Up @@ -122,7 +129,11 @@ public class PlaceholderExtractorCommand extends VisitorCommand {

@Inject
@BundleBee
private JsonProvider json;
private JsonBuilderFactory json;

@Inject
@BundleBee
private JsonProvider jsonProvider;

@Inject
private PlaceholderSpy placeholderSpy;
Expand Down Expand Up @@ -191,6 +202,7 @@ public CompletionStage<?> execute() {
return new Placeholder(
e.getKey(), defaultValues.size() == 1 ? defaultValues.get(0) : null, defaultValues);
})
.sorted(comparing(Placeholder::getName))
.collect(toList());

if (outputType == OutputType.ARGOCD) {
Expand Down Expand Up @@ -220,8 +232,37 @@ public CompletionStage<?> execute() {
},
JsonArrayBuilder::addAll,
JsonArrayBuilder::build));
System.out.println(argoCdDynamicConf);
System.out.println(argoCdDynamicConf); // ArgoCD reads stdout so writes it there
} else {
if (!"skip".equals(jsonFilename)) {
doWrite("JSON",
() -> Path.of(dumpLocation).resolve(jsonFilename), () -> json.createObjectBuilder()
.add("items", placeholders.stream()
.map(p -> {
final var key = p.getName();
final var desc = descriptions.getProperty(key, key);
final var defaultValue = p.getDefaultValue();
final var base = json.createObjectBuilder()
.add("name", key)
.add("description", desc);
if (defaultValue != null) {
base
.add("defaultValue", defaultValue)
.add("required", false);
} else {
base.add("required", true);
}
if (p.getDefaultValues() != null) {
base.add("defaultValues", p.getDefaultValues().stream()
.collect(Collector.of(json::createArrayBuilder, JsonArrayBuilder::add, JsonArrayBuilder::addAll)));
}
return base.build();
})
.collect(Collector.of(json::createArrayBuilder, JsonArrayBuilder::add, JsonArrayBuilder::addAll)))
.build()
.toString() + '\n');
}

if (!"skip".equals(propertiesFilename)) {
doWrite("Sample",
() -> Path.of(dumpLocation).resolve(propertiesFilename), () -> placeholders.stream()
Expand Down Expand Up @@ -274,7 +315,7 @@ protected String formatDoc(final List<Placeholder> placeholders, final Propertie
}

private String unescapeJson(final String value) {
try (final var reader = json.createReader(new StringReader(value))) {
try (final var reader = jsonProvider.createReader(new StringReader(value))) {
return ((JsonString) reader.readValue()).getString();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private CompletionStage<List<ManifestAndAlveolus>> doFindRootAlveoli(final Strin
log.info("" +
"Auto scanning the classpath, this can be dangerous if you don't fully control your classpath, " +
"ensure to set a particular alveolus if you doubt about this behavior");
return completedFuture(manifests(id)
return completedFuture(findManifestsFromClasspath(id)
.flatMap(m -> ofNullable(m.getAlveoli()).stream().flatMap(it -> it.stream().map(a -> new ManifestAndAlveolus(m, a))))
.collect(toList()));
}
Expand Down Expand Up @@ -276,7 +276,7 @@ private InputStream resolveManifestReference(final Path path, final String name)
}

private ManifestAndAlveolus findAlveolusInClasspath(final String alveolus, final String id) {
final var manifests = manifests(id).collect(toList());
final var manifests = findManifestsFromClasspath(id).collect(toList());
return manifests.stream()
.flatMap(m -> ofNullable(m.getAlveoli()).stream()
.flatMap(a -> a.stream().map(it -> new ManifestAndAlveolus(m, it))))
Expand All @@ -285,7 +285,7 @@ private ManifestAndAlveolus findAlveolusInClasspath(final String alveolus, final
.orElseThrow(() -> new IllegalStateException("No alveolus named '" + alveolus + "' found"));
}

private Stream<Manifest> manifests(final String id) {
public Stream<Manifest> findManifestsFromClasspath(final String id) {
final var classLoader = Thread.currentThread().getContextClassLoader();
try {
return list(classLoader
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2021 - present - Yupiik SAS - https://www.yupiik.com
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.yupiik.bundlebee.core.command.impl;

import io.yupiik.bundlebee.core.BundleBee;
import io.yupiik.bundlebee.core.test.BundleBeeExtension;
import io.yupiik.bundlebee.core.test.CommandExecutor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import static java.util.logging.Level.INFO;
import static org.junit.jupiter.api.Assertions.assertEquals;

class ListAlveoliCommandTest {
@RegisterExtension
BundleBeeExtension extension = new BundleBeeExtension();

@Test
void list(final CommandExecutor executor) {
final var logs = executor.wrap(null, INFO, () -> new BundleBee().launch("list-alveoli"));
assertEquals("Found alveoli:\n" +
"- ApplyCommandTest.apply\n" +
"- ApplyCommandTest.applyAwait\n" +
"- ApplyCommandTest.applyAwaitCondition\n" +
"- ApplyCommandTest.fromTemplate\n" +
"- ApplyCommandTest.includeIfPatch\n" +
"- ApplyCommandTest.simpleNestedDependencyWithReusingTheTemplate\n" +
"- ApplyCommandTest.template\n" +
"- ApplyCommandTest.withdep\n" +
"- ApplyCommandTest.withexclude\n" +
"- ApplyCommandTest.withsamedep\n" +
"- DeleteCommandTest.deleteMaven\n" +
"- RollbackCommandTest.first\n" +
"- RollbackCommandTest.second\n" +
"- customContentType\n" +
"\n", logs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ void extract(final CommandExecutor executor) {
final var logs = executor.wrap(null, INFO, () -> new BundleBee().launch(
"placeholder-extract", "--alveolus", "ApplyCommandTest.fromTemplate"));
assertEquals("" +
"JSON\n" +
"{\"items\":[{\"name\":\"ApplyCommandTest.fromTemplate.port\",\"description\":\"ApplyCommandTest.fromTemplate.port\",\"defaultValue\":\"9090\",\"required\":false,\"defaultValues\":[\"9090\"]},{\"name\":\"some.placeholder1\",\"description\":\"some.placeholder1\",\"defaultValue\":\"with defaultvalue\",\"required\":false,\"defaultValues\":[\"with defaultvalue\"]},{\"name\":\"some.placeholder2\",\"description\":\"some.placeholder2\",\"defaultValue\":\"with defaultvalue 2\",\"required\":false,\"defaultValues\":[\"with defaultvalue 2\"]}]}\n" +
"\n" +
"Sample\n" +
"# HELP: ApplyCommandTest.fromTemplate.port\n" +
"# ApplyCommandTest.fromTemplate.port = 9090\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ private List<Map.Entry<Path, String>> generate(final Path base, final Annotation
// see io.yupiik.bundlebee.maven.build.MojoGenerator.skipSharedConfig
private boolean skipSharedConfig(final String name) {
return "add-alveolus".equals(name) ||
"list-alveoli".equals(name) ||
"list-lint-rules".equals(name) ||
"cipher".equals(name) ||
"decipher".equals(name) ||
"build".equals(name) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ private static boolean skipSharedConfig(final String name) {
"build".equals(name) ||
"cipher-password".equals(name) ||
"create-master-password".equals(name) ||
"list-alveoli".equals(name) ||
"list-lint-rules".equals(name) ||
"new".equals(name) ||
"version".equals(name);
Expand Down

0 comments on commit 0ff996d

Please sign in to comment.