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

GH-644: documenter clean #680

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 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
Expand Up @@ -75,7 +75,6 @@ public class Documenter {

private static final Map<DependencyType, String> DEPENDENCY_DESCRIPTIONS = new LinkedHashMap<>();
private static final String INVALID_FILE_NAME_PATTERN = "Configured file name pattern does not include a '%s' placeholder for the module name!";
private static final String DEFAULT_LOCATION = "spring-modulith-docs";

private static final String DEFAULT_COMPONENTS_FILE = "components.puml";
private static final String DEFAULT_MODULE_COMPONENTS_FILE = "module-%s.puml";
Expand All @@ -89,7 +88,7 @@ public class Documenter {
private final Workspace workspace;
private final Container container;
private final ConfigurationProperties properties;
private final String outputFolder;
private final Options options;

private Map<ApplicationModule, Component> components;

Expand All @@ -110,22 +109,28 @@ public Documenter(Class<?> modulithType) {
* @param modules must not be {@literal null}.
*/
public Documenter(ApplicationModules modules) {
this(modules, getDefaultOutputDirectory());
this(modules, Options.defaults());
}

public Documenter(ApplicationModules modules, String outputFolder) {

this(modules, new Options(outputFolder, true));

Assert.hasText(outputFolder, "Output folder must not be null or empty!");
}

/**
* Creates a new {@link Documenter} for the given {@link ApplicationModules} and output folder.
*
* @param modules must not be {@literal null}.
* @param outputFolder must not be {@literal null} or empty.
* @param options must not be {@literal null}.
*/
public Documenter(ApplicationModules modules, String outputFolder) {
public Documenter(ApplicationModules modules, Options options) {

Assert.notNull(modules, "Modules must not be null!");
Assert.hasText(outputFolder, "Output folder must not be null or empty!");

this.modules = modules;
this.outputFolder = outputFolder;
this.options = options;
this.workspace = new Workspace("Modulith", "");

workspace.getViews().getConfiguration()
Expand Down Expand Up @@ -177,14 +182,18 @@ public Documenter writeDocumentation() {
* <li>The Module Canvas for each module.</li>
* </ul>
*
* @param options must not be {@literal null}.
* @param diagramOptions must not be {@literal null}.
* @param canvasOptions must not be {@literal null}.
* @return the current instance, will never be {@literal null}.
*/
public Documenter writeDocumentation(DiagramOptions options, CanvasOptions canvasOptions) {
public Documenter writeDocumentation(DiagramOptions diagramOptions, CanvasOptions canvasOptions) {

return writeModulesAsPlantUml(options)
.writeIndividualModulesAsPlantUml(options) //
if (this.options.clean) {
clearOutputFolder();
}

return writeModulesAsPlantUml(diagramOptions)
.writeIndividualModulesAsPlantUml(diagramOptions) //
.writeModuleCanvases(canvasOptions);
}

Expand Down Expand Up @@ -522,10 +531,26 @@ private ComponentView createComponentView(DiagramOptions options, @Nullable Appl
.createComponentView(container, prefix + options.toString(), "");
}

private void clearOutputFolder() {

Path outputPath = Paths.get(options.outputFolder);
if (!outputPath.toFile().exists()) {
return;
}

try (Stream<Path> paths = Files.walk(outputPath)) {
paths.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
} catch (IOException o_O) {
throw new RuntimeException(o_O);
}
}

private Path recreateFile(String name) {

try {

var outputFolder = options.outputFolder;

Files.createDirectories(Paths.get(outputFolder));
Path filePath = Paths.get(outputFolder, name);
Files.deleteIfExists(filePath);
Expand Down Expand Up @@ -574,15 +599,6 @@ private static <T> String addTableRow(List<T> types, String header, Function<Lis
return options.hideEmptyLines && types.isEmpty() ? "" : writeTableRow(header, mapper.apply(types));
}

/**
* Returns the default output directory based on the detected build system.
*
* @return will never be {@literal null}.
*/
private static String getDefaultOutputDirectory() {
return (new File("pom.xml").exists() ? "target" : "build").concat("/").concat(DEFAULT_LOCATION);
}

private static record Connection(Element source, Element target) {
public static Connection of(Relationship relationship) {
return new Connection(relationship.getSource(), relationship.getDestination());
Expand Down Expand Up @@ -1164,6 +1180,38 @@ boolean hasOnlyFallbackGroup() {
}
}

public static class Options {

private static final String DEFAULT_LOCATION = (new File("pom.xml").exists() ? "target" : "build")
.concat("/spring-modulith-docs");

private final String outputFolder;

private final boolean clean;

/**
* @param outputFolder the folder to write the files to, can be {@literal null}.
* @param clean whether to clean the target directory on rendering.
*/
private Options(@Nullable String outputFolder, boolean clean) {

this.outputFolder = outputFolder == null ? DEFAULT_LOCATION : outputFolder;
this.clean = clean;
}

public static Options defaults() {
return new Options(DEFAULT_LOCATION, true);
}

public Options withoutClean() {
return new Options(outputFolder, false);
}

public Options withOutputFolder(String folder) {
return new Options(folder, clean);
}
}

private static class CustomizedPlantUmlExporter extends StructurizrPlantUMLExporter {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.DependencyType;
import org.springframework.modulith.docs.Documenter.DiagramOptions;
import org.springframework.modulith.docs.Documenter.Options;

import com.acme.myproject.Application;

Expand Down Expand Up @@ -72,24 +74,47 @@ void testName() {
}

@Test
void customizesOutputLocation() throws IOException {
void customizesOutputLocation(@TempDir Path outputDirectory) throws IOException {
new Documenter(ApplicationModules.of(Application.class), outputDirectory.toString()).writeModuleCanvases();

String customOutputFolder = "build/spring-modulith";
Path path = Paths.get(customOutputFolder);
assertThat(Files.list(outputDirectory)).isNotEmpty();
assertThat(outputDirectory).exists();
}

@Test
void shouldCleanOutputLocation(@TempDir Path outputDirectory) throws IOException {
Path filePath = createTestFile(outputDirectory);
Path nestedFiledPath = createTestFileInSubdirectory(outputDirectory);

try {
new Documenter(ApplicationModules.of(Application.class), outputDirectory.toString()).writeDocumentation();

new Documenter(ApplicationModules.of(Application.class), customOutputFolder).writeModuleCanvases();
assertThat(filePath).doesNotExist();
assertThat(nestedFiledPath).doesNotExist();
assertThat(Files.list(outputDirectory)).isNotEmpty();
}

@Test
void shouldNotCleanOutputLocation(@TempDir Path outputDirectory) throws IOException {
Path filePath = createTestFile(outputDirectory);
Path nestedFiledPath = createTestFileInSubdirectory(outputDirectory);

assertThat(Files.list(path)).isNotEmpty();
assertThat(path).exists();
new Documenter(ApplicationModules.of(Application.class), Options.defaults().withOutputFolder(outputDirectory.toString()).withoutClean())
.writeDocumentation();

} finally {
assertThat(filePath).exists();
assertThat(nestedFiledPath).exists();
assertThat(Files.list(outputDirectory)).isNotEmpty();
}

private static Path createTestFile(Path tempDir) throws IOException {
return createFile(tempDir.resolve("some-old-module.adoc"));
}

private static Path createTestFileInSubdirectory(Path tempDir) throws IOException {
return createFile(tempDir.resolve("some-subdirectory").resolve("old-module.adoc"));
}

Files.walk(path)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
private static Path createFile(Path filePath) throws IOException {
return Files.createDirectories(filePath);
}
}
Loading