Skip to content

Commit 0ff996d

Browse files
committed
[command] add list-alveoli command plus enable to dump placeholders (per alveolus) in JSON
1 parent 2b7f11b commit 0ff996d

File tree

8 files changed

+255
-8
lines changed

8 files changed

+255
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright (c) 2021 - present - Yupiik SAS - https://www.yupiik.com
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing,
10+
* software distributed under the License is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12+
* KIND, either express or implied. See the License for the
13+
* specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.yupiik.bundlebee.core.command.impl;
17+
18+
import io.yupiik.bundlebee.core.command.Executable;
19+
import io.yupiik.bundlebee.core.configuration.Description;
20+
import io.yupiik.bundlebee.core.descriptor.Manifest;
21+
import io.yupiik.bundlebee.core.qualifier.BundleBee;
22+
import io.yupiik.bundlebee.core.service.AlveolusHandler;
23+
import lombok.Data;
24+
import lombok.extern.java.Log;
25+
import org.eclipse.microprofile.config.inject.ConfigProperty;
26+
27+
import javax.enterprise.context.Dependent;
28+
import javax.inject.Inject;
29+
import javax.json.JsonArrayBuilder;
30+
import javax.json.JsonBuilderFactory;
31+
import javax.json.bind.annotation.JsonbProperty;
32+
import java.io.IOException;
33+
import java.nio.file.Files;
34+
import java.nio.file.Path;
35+
import java.util.List;
36+
import java.util.concurrent.CompletionStage;
37+
import java.util.stream.Collector;
38+
import java.util.stream.Stream;
39+
40+
import static java.util.Comparator.comparing;
41+
import static java.util.stream.Collectors.joining;
42+
import static java.util.stream.Collectors.toList;
43+
44+
@Log
45+
@Dependent
46+
public class ListAlveoliCommand implements Executable {
47+
@Inject
48+
@Description("Root dependency to download to get the manifest. If set to `auto` it is assumed to be present in current classpath.")
49+
@ConfigProperty(name = "bundlebee.list-alveoli.from", defaultValue = "auto")
50+
private String from;
51+
52+
@Inject
53+
@Description("Manifest to load to start to find the alveolus. This optional setting mainly enables to use dependencies easily. " +
54+
"Ignored if set to `skip`.")
55+
@ConfigProperty(name = "bundlebee.list-alveoli.manifest", defaultValue = "skip")
56+
private String manifest;
57+
58+
@Inject
59+
@Description("`logger` means the standard bundlebee logging output stream else it is considered as a file path. " +
60+
"Note that if you end the filename with `.json` it will be formatted as json else just readable text.")
61+
@ConfigProperty(name = "bundlebee.list-alveoli.output", defaultValue = "logger")
62+
private String output;
63+
64+
@Inject
65+
private AlveolusHandler visitor;
66+
67+
@Inject
68+
@BundleBee
69+
private JsonBuilderFactory json;
70+
71+
@Override
72+
public String name() {
73+
return "list-alveoli";
74+
}
75+
76+
@Override
77+
public String description() {
78+
return "Lists all found alveoli";
79+
}
80+
81+
@Override
82+
public CompletionStage<?> execute() {
83+
return visitor.findManifest(from, manifest, null)
84+
.thenApply(m -> m == null || m.getAlveoli() == null ?
85+
visitor.findManifestsFromClasspath(null)
86+
.flatMap(it -> it.getAlveoli() == null ? Stream.empty() : it.getAlveoli().stream())
87+
.collect(toList()) :
88+
m.getAlveoli())
89+
.thenApply(items -> {
90+
switch (output) {
91+
case "logger":
92+
log.info(asText(items));
93+
break;
94+
case "logger.json":
95+
log.info(asJson(items));
96+
break;
97+
default:
98+
final var out = Path.of(output);
99+
try {
100+
if (out.getParent() != null) {
101+
Files.createDirectories(out.getParent());
102+
}
103+
Files.writeString(out, output.endsWith(".json") ?
104+
asJson(items) :
105+
asText(items));
106+
} catch (final IOException ioe) {
107+
throw new IllegalStateException(ioe);
108+
}
109+
}
110+
return items;
111+
});
112+
}
113+
114+
private String asJson(final List<Manifest.Alveolus> items) {
115+
return json.createObjectBuilder()
116+
.add("items", items.stream()
117+
.sorted(comparing(Manifest.Alveolus::getName))
118+
.map(it -> json.createObjectBuilder().add("name", it.getName()).build())
119+
.collect(Collector.of(
120+
json::createArrayBuilder,
121+
JsonArrayBuilder::add,
122+
JsonArrayBuilder::addAll)))
123+
.build()
124+
.toString();
125+
}
126+
127+
private static String asText(final List<Manifest.Alveolus> a) {
128+
return a.isEmpty() ?
129+
"No alveolus found." :
130+
a.stream()
131+
.sorted(comparing(Manifest.Alveolus::getName))
132+
.map(i -> "- " + i.getName())
133+
.collect(joining("\n", "Found alveoli:\n", "\n"));
134+
}
135+
136+
@Data
137+
public static class Item {
138+
@JsonbProperty
139+
private final String name;
140+
}
141+
142+
@Data
143+
public static class Items {
144+
@JsonbProperty
145+
private final List<Item> item;
146+
}
147+
}

bundlebee-core/src/main/java/io/yupiik/bundlebee/core/command/impl/ListLintCommand.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ public String description() {
4747

4848
@Override
4949
public CompletionStage<?> execute() {
50-
return completedFuture(checks.stream()
50+
final var output = checks.stream()
5151
.map(c -> " *" + c.name())
52-
.collect(joining("\n")));
52+
.collect(joining("\n"));
53+
log.info(output);
54+
return completedFuture(output);
5355
}
5456
}

bundlebee-core/src/main/java/io/yupiik/bundlebee/core/command/impl/PlaceholderExtractorCommand.java

+44-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import javax.enterprise.event.Observes;
2929
import javax.inject.Inject;
3030
import javax.json.JsonArrayBuilder;
31+
import javax.json.JsonBuilderFactory;
3132
import javax.json.JsonString;
3233
import javax.json.spi.JsonProvider;
3334
import java.io.IOException;
@@ -48,6 +49,7 @@
4849
import java.util.stream.Collector;
4950
import java.util.stream.Stream;
5051

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

100+
@Inject
101+
@Description("JSON filename (relative to `dumpLocation`) when `outputType` is `FILE`. Ignores JSON dump if value is `skip`.")
102+
@ConfigProperty(name = "bundlebee.placeholder-extract.jsonFilename", defaultValue = "placeholders.json")
103+
private String jsonFilename;
104+
98105
@Inject
99106
@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`.")
100107
@ConfigProperty(name = "bundlebee.placeholder-extract.completionFilename", defaultValue = "placeholders.completion.properties")
@@ -122,7 +129,11 @@ public class PlaceholderExtractorCommand extends VisitorCommand {
122129

123130
@Inject
124131
@BundleBee
125-
private JsonProvider json;
132+
private JsonBuilderFactory json;
133+
134+
@Inject
135+
@BundleBee
136+
private JsonProvider jsonProvider;
126137

127138
@Inject
128139
private PlaceholderSpy placeholderSpy;
@@ -191,6 +202,7 @@ public CompletionStage<?> execute() {
191202
return new Placeholder(
192203
e.getKey(), defaultValues.size() == 1 ? defaultValues.get(0) : null, defaultValues);
193204
})
205+
.sorted(comparing(Placeholder::getName))
194206
.collect(toList());
195207

196208
if (outputType == OutputType.ARGOCD) {
@@ -220,8 +232,37 @@ public CompletionStage<?> execute() {
220232
},
221233
JsonArrayBuilder::addAll,
222234
JsonArrayBuilder::build));
223-
System.out.println(argoCdDynamicConf);
235+
System.out.println(argoCdDynamicConf); // ArgoCD reads stdout so writes it there
224236
} else {
237+
if (!"skip".equals(jsonFilename)) {
238+
doWrite("JSON",
239+
() -> Path.of(dumpLocation).resolve(jsonFilename), () -> json.createObjectBuilder()
240+
.add("items", placeholders.stream()
241+
.map(p -> {
242+
final var key = p.getName();
243+
final var desc = descriptions.getProperty(key, key);
244+
final var defaultValue = p.getDefaultValue();
245+
final var base = json.createObjectBuilder()
246+
.add("name", key)
247+
.add("description", desc);
248+
if (defaultValue != null) {
249+
base
250+
.add("defaultValue", defaultValue)
251+
.add("required", false);
252+
} else {
253+
base.add("required", true);
254+
}
255+
if (p.getDefaultValues() != null) {
256+
base.add("defaultValues", p.getDefaultValues().stream()
257+
.collect(Collector.of(json::createArrayBuilder, JsonArrayBuilder::add, JsonArrayBuilder::addAll)));
258+
}
259+
return base.build();
260+
})
261+
.collect(Collector.of(json::createArrayBuilder, JsonArrayBuilder::add, JsonArrayBuilder::addAll)))
262+
.build()
263+
.toString() + '\n');
264+
}
265+
225266
if (!"skip".equals(propertiesFilename)) {
226267
doWrite("Sample",
227268
() -> Path.of(dumpLocation).resolve(propertiesFilename), () -> placeholders.stream()
@@ -274,7 +315,7 @@ protected String formatDoc(final List<Placeholder> placeholders, final Propertie
274315
}
275316

276317
private String unescapeJson(final String value) {
277-
try (final var reader = json.createReader(new StringReader(value))) {
318+
try (final var reader = jsonProvider.createReader(new StringReader(value))) {
278319
return ((JsonString) reader.readValue()).getString();
279320
}
280321
}

bundlebee-core/src/main/java/io/yupiik/bundlebee/core/service/AlveolusHandler.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private CompletionStage<List<ManifestAndAlveolus>> doFindRootAlveoli(final Strin
187187
log.info("" +
188188
"Auto scanning the classpath, this can be dangerous if you don't fully control your classpath, " +
189189
"ensure to set a particular alveolus if you doubt about this behavior");
190-
return completedFuture(manifests(id)
190+
return completedFuture(findManifestsFromClasspath(id)
191191
.flatMap(m -> ofNullable(m.getAlveoli()).stream().flatMap(it -> it.stream().map(a -> new ManifestAndAlveolus(m, a))))
192192
.collect(toList()));
193193
}
@@ -276,7 +276,7 @@ private InputStream resolveManifestReference(final Path path, final String name)
276276
}
277277

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

288-
private Stream<Manifest> manifests(final String id) {
288+
public Stream<Manifest> findManifestsFromClasspath(final String id) {
289289
final var classLoader = Thread.currentThread().getContextClassLoader();
290290
try {
291291
return list(classLoader
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2021 - present - Yupiik SAS - https://www.yupiik.com
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing,
10+
* software distributed under the License is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12+
* KIND, either express or implied. See the License for the
13+
* specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.yupiik.bundlebee.core.command.impl;
17+
18+
import io.yupiik.bundlebee.core.BundleBee;
19+
import io.yupiik.bundlebee.core.test.BundleBeeExtension;
20+
import io.yupiik.bundlebee.core.test.CommandExecutor;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.RegisterExtension;
23+
24+
import static java.util.logging.Level.INFO;
25+
import static org.junit.jupiter.api.Assertions.assertEquals;
26+
27+
class ListAlveoliCommandTest {
28+
@RegisterExtension
29+
BundleBeeExtension extension = new BundleBeeExtension();
30+
31+
@Test
32+
void list(final CommandExecutor executor) {
33+
final var logs = executor.wrap(null, INFO, () -> new BundleBee().launch("list-alveoli"));
34+
assertEquals("Found alveoli:\n" +
35+
"- ApplyCommandTest.apply\n" +
36+
"- ApplyCommandTest.applyAwait\n" +
37+
"- ApplyCommandTest.applyAwaitCondition\n" +
38+
"- ApplyCommandTest.fromTemplate\n" +
39+
"- ApplyCommandTest.includeIfPatch\n" +
40+
"- ApplyCommandTest.simpleNestedDependencyWithReusingTheTemplate\n" +
41+
"- ApplyCommandTest.template\n" +
42+
"- ApplyCommandTest.withdep\n" +
43+
"- ApplyCommandTest.withexclude\n" +
44+
"- ApplyCommandTest.withsamedep\n" +
45+
"- DeleteCommandTest.deleteMaven\n" +
46+
"- RollbackCommandTest.first\n" +
47+
"- RollbackCommandTest.second\n" +
48+
"- customContentType\n" +
49+
"\n", logs);
50+
}
51+
}

bundlebee-core/src/test/java/io/yupiik/bundlebee/core/command/impl/PlaceholderExtractorCommandTest.java

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ void extract(final CommandExecutor executor) {
3333
final var logs = executor.wrap(null, INFO, () -> new BundleBee().launch(
3434
"placeholder-extract", "--alveolus", "ApplyCommandTest.fromTemplate"));
3535
assertEquals("" +
36+
"JSON\n" +
37+
"{\"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" +
38+
"\n" +
3639
"Sample\n" +
3740
"# HELP: ApplyCommandTest.fromTemplate.port\n" +
3841
"# ApplyCommandTest.fromTemplate.port = 9090\n" +

bundlebee-documentation/src/main/java/io/yupiik/bundlebee/documentation/CommandConfigurationGenerator.java

+2
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ private List<Map.Entry<Path, String>> generate(final Path base, final Annotation
141141
// see io.yupiik.bundlebee.maven.build.MojoGenerator.skipSharedConfig
142142
private boolean skipSharedConfig(final String name) {
143143
return "add-alveolus".equals(name) ||
144+
"list-alveoli".equals(name) ||
145+
"list-lint-rules".equals(name) ||
144146
"cipher".equals(name) ||
145147
"decipher".equals(name) ||
146148
"build".equals(name) ||

bundlebee-maven-plugin/src/main/java/io/yupiik/bundlebee/maven/build/MojoGenerator.java

+1
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ private static boolean skipSharedConfig(final String name) {
311311
"build".equals(name) ||
312312
"cipher-password".equals(name) ||
313313
"create-master-password".equals(name) ||
314+
"list-alveoli".equals(name) ||
314315
"list-lint-rules".equals(name) ||
315316
"new".equals(name) ||
316317
"version".equals(name);

0 commit comments

Comments
 (0)