Skip to content

Commit 570f347

Browse files
committed
GH-713 - Polishing.
1 parent b94d13b commit 570f347

File tree

4 files changed

+90
-82
lines changed

4 files changed

+90
-82
lines changed

spring-modulith-docs/src/main/java/org/springframework/modulith/docs/Asciidoctor.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,17 +369,14 @@ public String renderBeanReferences(ApplicationModule module) {
369369
}
370370

371371
public String renderHeadline(int i, String modules) {
372-
373372
return "=".repeat(i) + " " + modules + System.lineSeparator();
374373
}
375374

376375
public String renderPlantUmlInclude(String componentsFilename) {
377-
378376
return "plantuml::" + componentsFilename + "[]" + System.lineSeparator();
379377
}
380378

381379
public String renderGeneralInclude(String componentsFilename) {
382-
383380
return "include::" + componentsFilename + "[]" + System.lineSeparator();
384381
}
385382
}

spring-modulith-docs/src/main/java/org/springframework/modulith/docs/Documenter.java

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
* API to create documentation for {@link ApplicationModules}.
7171
*
7272
* @author Oliver Drotbohm
73+
* @author Cora Iberkleid
7374
*/
7475
public class Documenter {
7576

@@ -134,7 +135,7 @@ public Documenter(ApplicationModules modules, String outputFolder) {
134135
.shape(Shape.Component);
135136

136137
Model model = workspace.getModel();
137-
String systemName = modules.getSystemName().orElse("Modulith");
138+
String systemName = getDefaultedSystemName();
138139

139140
SoftwareSystem system = model.addSoftwareSystem(systemName, "");
140141

@@ -190,13 +191,13 @@ public Documenter writeDocumentation(DiagramOptions options, CanvasOptions canva
190191
}
191192

192193
/**
193-
* Writes aggregating document called 'all-docs.adoc' that includes any existing component diagrams and canvases.
194-
* using {@link DiagramOptions#defaults()} and {@link CanvasOptions#defaults()}.
194+
* Writes aggregating document called {@code all-docs.adoc} that includes any existing component diagrams and
195+
* canvases. using {@link DiagramOptions#defaults()} and {@link CanvasOptions#defaults()}.
195196
*
196197
* @return the current instance, will never be {@literal null}.
198+
* @since 1.2.2
197199
*/
198-
public Documenter writeAggregatingDocument(){
199-
200+
public Documenter writeAggregatingDocument() {
200201
return writeAggregatingDocument(DiagramOptions.defaults(), CanvasOptions.defaults());
201202
}
202203

@@ -206,8 +207,9 @@ public Documenter writeAggregatingDocument(){
206207
* @param options must not be {@literal null}.
207208
* @param canvasOptions must not be {@literal null}.
208209
* @return the current instance, will never be {@literal null}.
210+
* @since 1.2.2
209211
*/
210-
public Documenter writeAggregatingDocument(DiagramOptions options, CanvasOptions canvasOptions){
212+
public Documenter writeAggregatingDocument(DiagramOptions options, CanvasOptions canvasOptions) {
211213

212214
Assert.notNull(options, "DiagramOptions must not be null!");
213215
Assert.notNull(canvasOptions, "CanvasOptions must not be null!");
@@ -220,7 +222,9 @@ public Documenter writeAggregatingDocument(DiagramOptions options, CanvasOptions
220222
var componentsDoc = new StringBuilder();
221223

222224
if (outputFolder.contains(componentsFilename)) {
223-
componentsDoc.append(asciidoctor.renderHeadline(2, modules.getSystemName().orElse("Modules")))
225+
226+
componentsDoc
227+
.append(asciidoctor.renderHeadline(2, getDefaultedSystemName()))
224228
.append(asciidoctor.renderPlantUmlInclude(componentsFilename))
225229
.append(System.lineSeparator());
226230
}
@@ -230,25 +234,20 @@ public Documenter writeAggregatingDocument(DiagramOptions options, CanvasOptions
230234

231235
// Get diagram file name, e.g. module-inventory.puml
232236
var fileNamePattern = options.getTargetFileName().orElse(DEFAULT_MODULE_COMPONENTS_FILE);
233-
Assert.isTrue(fileNamePattern.contains("%s"), () -> String.format(INVALID_FILE_NAME_PATTERN, fileNamePattern));
234-
var filename = String.format(fileNamePattern, it.getName());
235-
236-
// Get canvas file name, e.g. module-inventory.adoc
237+
var filename = fileNamePattern.formatted(it.getName());
237238
var canvasFilename = canvasOptions.getTargetFileName(it.getName());
238-
239-
// Generate output, e.g.:
240-
/*
241-
== Inventory
242-
plantuml::module-inventory.puml[]
243-
include::module-inventory.adoc[]
244-
*/
245239
var content = new StringBuilder();
246-
content.append((outputFolder.contains(filename) ? asciidoctor.renderPlantUmlInclude(filename) : ""))
247-
.append((outputFolder.contains(canvasFilename) ? asciidoctor.renderGeneralInclude(canvasFilename) : ""));
240+
241+
content.append(outputFolder.contains(filename) ? asciidoctor.renderPlantUmlInclude(filename) : "")
242+
.append(outputFolder.contains(canvasFilename) ? asciidoctor.renderGeneralInclude(canvasFilename) : "");
243+
248244
if (!content.isEmpty()) {
249-
content.insert(0, asciidoctor.renderHeadline(2, it.getDisplayName()))
245+
246+
content
247+
.insert(0, asciidoctor.renderHeadline(2, it.getDisplayName()))
250248
.append(System.lineSeparator());
251249
}
250+
252251
return content.toString();
253252

254253
}).collect(Collectors.joining());
@@ -257,7 +256,8 @@ public Documenter writeAggregatingDocument(DiagramOptions options, CanvasOptions
257256

258257
// Write file to all-docs.adoc
259258
if (!allDocs.isBlank()) {
260-
Path file = recreateFile("all-docs.adoc");
259+
260+
var file = recreateFile("all-docs.adoc");
261261

262262
try (Writer writer = new FileWriter(file.toFile())) {
263263
writer.write(allDocs);
@@ -351,9 +351,7 @@ public Documenter writeModuleAsPlantUml(ApplicationModule module, DiagramOptions
351351

352352
var fileNamePattern = options.getTargetFileName().orElse(DEFAULT_MODULE_COMPONENTS_FILE);
353353

354-
Assert.isTrue(fileNamePattern.contains("%s"), () -> String.format(INVALID_FILE_NAME_PATTERN, fileNamePattern));
355-
356-
return writeViewAsPlantUml(view, String.format(fileNamePattern, module.getName()), options);
354+
return writeViewAsPlantUml(view, fileNamePattern.formatted(module.getName()), options);
357355
}
358356

359357
/**
@@ -584,7 +582,7 @@ private String render(ComponentView view, DiagramOptions options) {
584582
private String createPlantUml(DiagramOptions options) {
585583

586584
ComponentView componentView = createComponentView(options);
587-
componentView.setTitle(modules.getSystemName().orElse("Modules"));
585+
componentView.setTitle(getDefaultedSystemName());
588586

589587
addComponentsToView(() -> modules.stream(), componentView, options, it -> {});
590588

@@ -664,6 +662,10 @@ private static String getDefaultOutputDirectory() {
664662
return (new File("pom.xml").exists() ? "target" : "build").concat("/").concat(DEFAULT_LOCATION);
665663
}
666664

665+
private String getDefaultedSystemName() {
666+
return modules.getSystemName().orElse("Modules");
667+
}
668+
667669
private static record Connection(Element source, Element target) {
668670
public static Connection of(Relationship relationship) {
669671
return new Connection(relationship.getSource(), relationship.getDestination());
@@ -771,6 +773,9 @@ public DiagramOptions withTargetOnly(Predicate<ApplicationModule> targetOnly) {
771773
* include a {@code %s} placeholder for the module names.
772774
*/
773775
public DiagramOptions withTargetFileName(String targetFileName) {
776+
777+
Assert.isTrue(targetFileName.contains("%s"), () -> INVALID_FILE_NAME_PATTERN.formatted(targetFileName));
778+
774779
return new DiagramOptions(dependencyTypes, dependencyDepth, exclusions, componentFilter, targetOnly,
775780
targetFileName, colorSelector, defaultDisplayName, style, elementsWithoutRelationships);
776781
}
@@ -1263,12 +1268,12 @@ private static class OutputFolder {
12631268

12641269
private final String path;
12651270

1266-
OutputFolder(String path) {
1267-
this.path = path;
1268-
}
1271+
OutputFolder(String path) {
1272+
this.path = path;
1273+
}
12691274

12701275
boolean contains(String filename) {
12711276
return Files.exists(Paths.get(path, filename));
12721277
}
1273-
}
1278+
}
12741279
}

spring-modulith-integration-test/src/test/java/org/springframework/modulith/docs/DocumenterTest.java

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
import org.springframework.modulith.core.ApplicationModules;
3232
import org.springframework.modulith.core.DependencyType;
3333
import org.springframework.modulith.docs.Documenter.DiagramOptions;
34+
import org.springframework.util.function.ThrowingConsumer;
3435

3536
import com.acme.myproject.Application;
3637

3738
/**
3839
* Unit tests for {@link Documenter}.
3940
*
4041
* @author Oliver Drotbohm
42+
* @author Cora Iberkleid
4143
*/
4244
class DocumenterTest {
4345

@@ -73,75 +75,68 @@ void testName() {
7375
}
7476

7577
@Test
76-
void customizesOutputLocation() throws IOException {
78+
void customizesOutputLocation() throws Exception {
7779

7880
String customOutputFolder = "build/spring-modulith";
7981
Path path = Paths.get(customOutputFolder);
8082

81-
try {
83+
doWith(path, it -> {
8284

8385
new Documenter(ApplicationModules.of(Application.class), customOutputFolder).writeModuleCanvases();
8486

8587
assertThat(Files.list(path)).isNotEmpty();
8688
assertThat(path).exists();
87-
88-
} finally {
89-
90-
deleteDirectory(path);
91-
}
89+
});
9290
}
9391

9492
@Test // GH-638
95-
void writesAggregatingDocumentOnlyIfOtherDocsExist() throws IOException {
96-
97-
String customOutputFolder = "build/spring-modulith";
98-
Path path = Paths.get(customOutputFolder);
93+
void createsAggregatingDocumentOnlyIfPartialsExist() throws Exception {
9994

100-
Documenter documenter = new Documenter(ApplicationModules.of(Application.class), customOutputFolder);
95+
var customOutputFolder = "build/spring-modulith";
96+
var path = Paths.get(customOutputFolder);
97+
var documenter = new Documenter(ApplicationModules.of(Application.class), customOutputFolder);
10198

102-
try {
99+
doWith(path, it -> {
103100

104101
// all-docs.adoc should be created
105102
documenter.writeDocumentation();
106103

107-
// Count files
108-
long actualFiles;
109-
try (Stream<Path> stream = Files.walk(path)) {
110-
actualFiles = stream.filter(Files::isRegularFile).count();
111-
}
112-
// Expect 2 files per module plus components diagram and all-docs.adoc
113-
long expectedFiles = (documenter.getModules().stream().count() * 2) + 2;
114-
assertThat(actualFiles).isEqualTo(expectedFiles);
115-
116-
Optional<Path> optionalPath = Files.walk(path)
117-
.filter(p -> p.getFileName().toString().equals("all-docs.adoc"))
118-
.findFirst();
119-
assertThat(optionalPath.isPresent());
120-
121-
// Count non-blank lines in all-docs.adoc
122-
long actualLines;
123-
try (Stream<String> lines = Files.lines(optionalPath.get())) {
124-
actualLines = lines.filter(line -> !line.trim().isEmpty())
125-
.count();
126-
}
127-
// Expect 3 lines per module and 2 lines for components
128-
long expectedLines = (documenter.getModules().stream().count() * 3) + 2;
129-
assertThat(actualLines).isEqualTo(expectedLines);
104+
// 2 per module (PlantUML + Canvas) + component overview + aggregating doc
105+
var expectedFiles = documenter.getModules().stream().count() * 2 + 2;
130106

131-
// all-docs.adoc should not be created
132-
deleteDirectoryContents(path);
107+
// 3 per module (headline + PlantUML + Canvas) + component headline + component PlantUML
108+
var expectedLines = documenter.getModules().stream().count() * 3 + 2;
133109

134-
documenter.writeAggregatingDocument();
110+
assertThat(Files.walk(it).filter(Files::isRegularFile).count())
111+
.isEqualTo(expectedFiles);
135112

136-
optionalPath = Files.walk(path)
137-
.filter(p -> p.getFileName().toString().equals("all-docs.adoc"))
138-
.findFirst();
139-
assertThat(optionalPath.isEmpty());
113+
assertThat(path.resolve("all-docs.adoc")).exists().satisfies(doc -> {
114+
assertThat(Files.lines(doc)
115+
.filter(line -> !line.trim().isEmpty())
116+
.count()).isEqualTo(expectedLines);
117+
});
118+
});
119+
}
140120

141-
} finally {
121+
@Test // GH-638
122+
void doesNotCreateAggregatingDocumentIfNoPartialsExist() throws Exception {
142123

143-
deleteDirectory(path);
144-
}
124+
var customOutputFolder = "build/spring-modulith";
125+
var path = Paths.get(customOutputFolder);
126+
127+
doWith(path, it -> {
128+
129+
var documenter = new Documenter(ApplicationModules.of(Application.class), customOutputFolder)
130+
.writeDocumentation();
131+
132+
deleteDirectoryContents(it);
133+
134+
documenter.writeAggregatingDocument();
135+
136+
var aggregatingDoc = path.resolve("all-docs.adoc");
137+
138+
assertThat(aggregatingDoc).doesNotExist();
139+
});
145140
}
146141

147142
private static void deleteDirectoryContents(Path path) throws IOException {
@@ -161,4 +156,13 @@ private static void deleteDirectory(Path path) throws IOException {
161156
deleteDirectoryContents(path);
162157
Files.deleteIfExists(path);
163158
}
159+
160+
private static void doWith(Path path, ThrowingConsumer<Path> consumer) throws Exception {
161+
162+
try {
163+
consumer.accept(path);
164+
} finally {
165+
deleteDirectory(path);
166+
}
167+
}
164168
}

src/docs/antora/modules/ROOT/pages/documentation.adoc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ Requires the usage of the `spring-boot-configuration-processor` artifact to extr
308308
[[aggregating-document]]
309309
== Generating an Aggregating Document
310310

311-
The aggregating document can be generated by calling `Documenter.writeAggregatingDocument()`:
311+
When using `Documenter.writeDocumentation(…)` an `all-docs.adoc` file will be generated, linking all generated diagrams and Application Module Canvases.
312+
We can manually generate the aggregating document by calling `Documenter.writeAggregatingDocument()`:
312313

313314
.Generating an aggregating document using `Documenter`
314315
[tabs]
@@ -325,7 +326,7 @@ class DocumentationTests {
325326
void writeDocumentationSnippets() {
326327
327328
new Documenter(modules)
328-
.writeAggregatingDocument();
329+
.writeAggregatingDocument();
329330
}
330331
}
331332
----
@@ -346,4 +347,5 @@ class DocumentationTests {
346347
----
347348
======
348349

349-
The aggregating document will include any existing application module component diagrams and application module canvases. If there are none, then this method will not produce an output file.
350+
The aggregating document will include any existing application module component diagrams and application module canvases.
351+
If there are none, then this method will not produce an output file.

0 commit comments

Comments
 (0)