From b09a3aee97344dda8929a683535b89aed2bc521e Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 14 Nov 2022 10:35:29 +0100 Subject: [PATCH 1/4] Generate pre-generated diagrams without links Those are meant for downloading, having links in them in kind-of useless. --- .../structurizr/site/generatr/GenerateSiteCommand.kt | 4 ++-- .../nl/avisi/structurizr/site/generatr/ServeCommand.kt | 2 +- .../structurizr/site/generatr/site/DiagramGenerator.kt | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/GenerateSiteCommand.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/GenerateSiteCommand.kt index 67268dee..afa3e12b 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/GenerateSiteCommand.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/GenerateSiteCommand.kt @@ -81,7 +81,7 @@ class GenerateSiteCommand : Subcommand( clonedRepository.checkoutBranch(branch) val workspace = createStructurizrWorkspace(workspaceFileInRepo) - generateDiagrams(workspace, File(siteDir, branch), branch) + generateDiagrams(workspace, File(siteDir, branch)) generateSite( version, workspace, @@ -95,7 +95,7 @@ class GenerateSiteCommand : Subcommand( private fun generateSiteForModel(siteDir: File) { val workspace = createStructurizrWorkspace(File(workspaceFile)) - generateDiagrams(workspace, File(siteDir, defaultBranch), defaultBranch) + generateDiagrams(workspace, File(siteDir, defaultBranch)) generateSite( version, workspace, diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt index 6ddcd0ec..22072f9a 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/ServeCommand.kt @@ -52,7 +52,7 @@ class ServeCommand : Subcommand("serve", "Start a development server") { val exportDir = File(siteDir, branch) println("Generating diagrams...") - generateDiagrams(workspace, exportDir, branch) + generateDiagrams(workspace, exportDir) println("Generating site...") copySiteWideAssets(File(siteDir)) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt index 678cd373..2fd6ad29 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt @@ -2,6 +2,7 @@ package nl.avisi.structurizr.site.generatr.site import com.structurizr.Workspace import com.structurizr.export.Diagram +import com.structurizr.export.plantuml.C4PlantUMLExporter import com.structurizr.export.plantuml.PlantUMLDiagram import net.sourceforge.plantuml.FileFormat import net.sourceforge.plantuml.FileFormatOption @@ -9,12 +10,12 @@ import net.sourceforge.plantuml.SourceStringReader import java.io.File import java.net.URL -fun generateDiagrams(workspace: Workspace, exportDir: File, branch: String) { +fun generateDiagrams(workspace: Workspace, exportDir: File) { val pumlDir = File(exportDir, "puml").apply { mkdirs() } val pngDir = File(exportDir, "png").apply { mkdirs() } val svgDir = File(exportDir, "svg").apply { mkdirs() } - val plantUMLDiagrams = generatePlantUMLDiagrams(workspace, branch) + val plantUMLDiagrams = generatePlantUMLDiagrams(workspace) plantUMLDiagrams.parallelStream() .forEach { diagram -> @@ -29,8 +30,8 @@ fun generateDiagrams(workspace: Workspace, exportDir: File, branch: String) { } } -private fun generatePlantUMLDiagrams(workspace: Workspace, branch: String): Collection { - val plantUMLExporter = C4PlantUmlExporterWithElementLinks(workspace, branch) +private fun generatePlantUMLDiagrams(workspace: Workspace): Collection { + val plantUMLExporter = C4PlantUMLExporter() return plantUMLExporter.export(workspace) } From aefd63b3153c18addcc31bfa07dd14681cc9d636 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 14 Nov 2022 11:07:07 +0100 Subject: [PATCH 2/4] Make element links in embedded SVGs relative This fixes broken links when the generate site is not hosted as root web site. As a consequence we no longer generate embedded SVG's up-front, but generate them as we need them with links relative to the link of the current page. To preserve generation speed for serve, we try to cache as much as we can. --- .../C4PlantUmlExporterWithElementLinks.kt | 26 +++++++- .../site/generatr/site/DiagramGenerator.kt | 59 ++++++++++++++++--- .../site/generatr/site/GeneratorContext.kt | 2 +- .../site/generatr/site/SiteGenerator.kt | 9 +-- .../generatr/site/model/DiagramViewModel.kt | 4 +- .../generatr/site/model/MarkdownViewModel.kt | 2 +- .../site/generatr/site/views/Markdown.kt | 9 ++- .../C4PlantUmlExporterWithElementLinksTest.kt | 51 ++++++++++++---- .../site/generatr/site/model/ViewModelTest.kt | 2 +- 9 files changed, 125 insertions(+), 39 deletions(-) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt index e8b1fe18..dfd03984 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt @@ -1,20 +1,39 @@ package nl.avisi.structurizr.site.generatr.site import com.structurizr.Workspace +import com.structurizr.export.Diagram import com.structurizr.export.IndentingWriter import com.structurizr.export.plantuml.C4PlantUMLExporter import com.structurizr.model.Element import com.structurizr.model.SoftwareSystem +import com.structurizr.view.ComponentView +import com.structurizr.view.ContainerView +import com.structurizr.view.CustomView +import com.structurizr.view.DeploymentView +import com.structurizr.view.DynamicView +import com.structurizr.view.SystemContextView +import com.structurizr.view.SystemLandscapeView import com.structurizr.view.View import nl.avisi.structurizr.site.generatr.includedSoftwareSystems import nl.avisi.structurizr.site.generatr.normalize class C4PlantUmlExporterWithElementLinks( private val workspace: Workspace, - private val branch: String + private val url: String ): C4PlantUMLExporter() { companion object { - const val TEMP_URI = "https://will-be-changed-to-relative" + const val TEMP_URI = "https://will-be-changed-to-relative/" + + fun C4PlantUMLExporter.export(view: View): Diagram = when (view) { + is CustomView -> export(view) + is SystemLandscapeView -> export(view) + is SystemContextView -> export(view) + is ContainerView -> export(view) + is ComponentView -> export(view) + is DynamicView -> export(view) + is DeploymentView -> export(view) + else -> throw IllegalStateException("View ${view.name} has a non-exportable type") + } } override fun writeElement(view: View?, element: Element?, writer: IndentingWriter?) { @@ -30,7 +49,8 @@ class C4PlantUmlExporterWithElementLinks( workspace.model.includedSoftwareSystems.contains(this) && this != view?.softwareSystem private fun setElementUrl(element: Element) { - element.url = "${TEMP_URI}/$branch/${element.name.normalize()}/context/" + val path = "/${element.name.normalize()}/context/".asUrlRelativeTo(url) + element.url = "${TEMP_URI}$path" } private fun writeModifiedElement( diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt index 2fd6ad29..deb05e06 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt @@ -4,16 +4,18 @@ import com.structurizr.Workspace import com.structurizr.export.Diagram import com.structurizr.export.plantuml.C4PlantUMLExporter import com.structurizr.export.plantuml.PlantUMLDiagram +import com.structurizr.view.View import net.sourceforge.plantuml.FileFormat import net.sourceforge.plantuml.FileFormatOption import net.sourceforge.plantuml.SourceStringReader +import nl.avisi.structurizr.site.generatr.site.C4PlantUmlExporterWithElementLinks.Companion.export import java.io.File import java.net.URL fun generateDiagrams(workspace: Workspace, exportDir: File) { - val pumlDir = File(exportDir, "puml").apply { mkdirs() } - val pngDir = File(exportDir, "png").apply { mkdirs() } - val svgDir = File(exportDir, "svg").apply { mkdirs() } + val pumlDir = pumlDir(exportDir) + val svgDir = svgDir(exportDir) + val pngDir = pngDir(exportDir) val plantUMLDiagrams = generatePlantUMLDiagrams(workspace) @@ -23,13 +25,32 @@ fun generateDiagrams(workspace: Workspace, exportDir: File) { if (!plantUMLFile.exists() || plantUMLFile.readText() != diagram.definition) { println("${diagram.key}...") saveAsPUML(diagram, plantUMLFile) - saveImages(diagram, pngDir, svgDir) + saveAsSvg(diagram, svgDir) + saveAsPng(diagram, pngDir) } else { println("${diagram.key} UP-TO-DATE") } } } +fun generateDiagramWithElementLinks(workspace: Workspace, exportDir: File, view: View, baseUrl: String): String { + val pumlDir = pumlDir(exportDir) + val svgDir = svgDir(exportDir) + + val diagram = generatePlantUMLDiagramWithElementLinks(workspace, baseUrl, view) + + val name = "${diagram.key}-${view.key}" + val plantUMLFile = File(pumlDir, "$name.puml") + if (!plantUMLFile.exists() || plantUMLFile.readText() != diagram.definition) { + saveAsPUML(diagram, plantUMLFile) + saveAsSvg(diagram, svgDir, name) + } else { + println("$name UP-TO-DATE") + } + + return readSvg(svgDir, name) +} + private fun generatePlantUMLDiagrams(workspace: Workspace): Collection { val plantUMLExporter = C4PlantUMLExporter() @@ -40,19 +61,39 @@ private fun saveAsPUML(diagram: Diagram, plantUMLFile: File) { plantUMLFile.writeText(diagram.definition) } -private fun saveImages(diagram: Diagram, pngDir: File, svgDir: File) { +private fun saveAsSvg(diagram: Diagram, svgDir: File, name: String = diagram.key) { + val reader = SourceStringReader(diagram.withCachedIncludes().definition) + val svgFile = File(svgDir, "$name.svg") + + svgFile.outputStream().use { + reader.outputImage(it, FileFormatOption(FileFormat.SVG, false)) + } +} + +private fun saveAsPng(diagram: Diagram, pngDir: File) { val reader = SourceStringReader(diagram.withCachedIncludes().definition) val pngFile = File(pngDir, "${diagram.key}.png") - val svgFile = File(svgDir, "${diagram.key}.svg") pngFile.outputStream().use { reader.outputImage(it) } - svgFile.outputStream().use { - reader.outputImage(it, FileFormatOption(FileFormat.SVG, false)) - } } +private fun readSvg(svgDir: File, name: String): String { + val svgFile = File(svgDir, "$name.svg") + return svgFile.readText() +} + +private fun generatePlantUMLDiagramWithElementLinks(workspace: Workspace, baseUrl: String, view: View ): Diagram { + val plantUMLExporter = C4PlantUmlExporterWithElementLinks(workspace, baseUrl) + + return plantUMLExporter.export(view) +} + +private fun pumlDir(exportDir: File) = File(exportDir, "puml").apply { mkdirs() } +private fun svgDir(exportDir: File) = File(exportDir, "svg").apply { mkdirs() } +private fun pngDir(exportDir: File) = File(exportDir, "png").apply { mkdirs() } + private fun Diagram.withCachedIncludes(): Diagram { val def = definition.replace("!include\\s+(.*)".toRegex()) { val cachedInclude = IncludeCache.cachedInclude(it.groupValues[1]) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/GeneratorContext.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/GeneratorContext.kt index 041a20b4..ee053c45 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/GeneratorContext.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/GeneratorContext.kt @@ -7,5 +7,5 @@ data class GeneratorContext( val workspace: Workspace, val branches: List, val currentBranch: String, - val svgFactory: (name: String) -> String + val svgFactory: (key: String, url: String) -> String ) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt index 6f130c0b..d1ca4381 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt @@ -45,12 +45,9 @@ fun generateSite( branches: List, currentBranch: String ) { - val generatorContext = GeneratorContext(version, workspace, branches, currentBranch) { - val pathname = "${exportDir.absolutePath}/${currentBranch}/svg/${it}.svg" - File(pathname) - .let { file -> - if (file.exists()) file.readText() else "$pathname not found" - } + val generatorContext = GeneratorContext(version, workspace, branches, currentBranch) { name, baseUrl -> + val view = workspace.views.views.single { view -> view.key == name } + generateDiagramWithElementLinks(workspace, exportDir, view, baseUrl) } if (assetsDir != null) copyAssets(assetsDir, File(exportDir, currentBranch)) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DiagramViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DiagramViewModel.kt index c45d8297..a8f829ee 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DiagramViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DiagramViewModel.kt @@ -10,9 +10,9 @@ data class DiagramViewModel( val pumlLocation: ImageViewModel ) { companion object { - fun forView(pageViewModel: PageViewModel, view: View, svgFactory: (name: String) -> String) = DiagramViewModel( + fun forView(pageViewModel: PageViewModel, view: View, svgFactory: (key: String, url: String) -> String) = DiagramViewModel( view.name, - svgFactory(view.key), + svgFactory(view.key, pageViewModel.url), ImageViewModel(pageViewModel, "/svg/${view.key}.svg"), ImageViewModel(pageViewModel, "/png/${view.key}.png"), ImageViewModel(pageViewModel, "/puml/${view.key}.puml") diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/MarkdownViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/MarkdownViewModel.kt index 4335b2b9..7fc3044d 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/MarkdownViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/MarkdownViewModel.kt @@ -1,3 +1,3 @@ package nl.avisi.structurizr.site.generatr.site.model -data class MarkdownViewModel(val markdown: String, val svgFactory: (name: String) -> String) +data class MarkdownViewModel(val markdown: String, val svgFactory: (key: String, url: String) -> String) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Markdown.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Markdown.kt index 12686060..7b04f978 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Markdown.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Markdown.kt @@ -40,7 +40,7 @@ private fun markdownToHtml(pageViewModel: PageViewModel, markdownViewModel: Mark val html = renderer.render(markDownDocument) return Jsoup.parse(html) - .apply { body().transformEmbeddedDiagramElements(markdownViewModel.svgFactory) } + .apply { body().transformEmbeddedDiagramElements(markdownViewModel.svgFactory, pageViewModel.url) } .html() } @@ -71,11 +71,14 @@ private class CustomLinkResolver(private val pageViewModel: PageViewModel) : Lin } } -private fun Element.transformEmbeddedDiagramElements(svgFactory: (name: String) -> String) = this.allElements +private fun Element.transformEmbeddedDiagramElements( + svgFactory: (key: String, url: String) -> String, + url: String +) = this.allElements .toList() .filter { it.tag().name == "img" && it.attr("src").startsWith("embed:") } .forEach { val diagramId = it.attr("src").substring(6) - it.parent()?.append(svgFactory(diagramId)) + it.parent()?.append(svgFactory(diagramId, url)) it.remove() } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt index e23a5b02..c3c0cc27 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt @@ -3,18 +3,16 @@ package nl.avisi.structurizr.site.generatr.site import assertk.assertThat import assertk.assertions.isEqualTo import com.structurizr.Workspace -import com.structurizr.model.SoftwareSystem +import com.structurizr.view.SystemContextView import org.junit.jupiter.api.Test class C4PlantUmlExporterWithElementLinksTest { @Test fun `renders diagram`() { - val (workspace, system) = createWorkspace() - val view = workspace.views.createSystemContextView(system, "Context1", "") - .apply { addAllElements() } + val (workspace, view) = createWorkspaceWithOneSystem() - val diagram = C4PlantUmlExporterWithElementLinks(workspace, "master") + val diagram = C4PlantUmlExporterWithElementLinks(workspace, "/landscape/") .export(view) assertThat(diagram.definition.withoutHeaderAndFooter()).isEqualTo( @@ -26,28 +24,55 @@ class C4PlantUmlExporterWithElementLinksTest { @Test fun `link to other software system`() { - val (workspace, system) = createWorkspace() - workspace.model.addSoftwareSystem("System 2").apply { uses(system, "uses") } - val view = workspace.views.createSystemContextView(system, "Context 1", "") - .apply { addAllElements() } + val (workspace, view) = createWorkspaceWithTwoSystems() - val diagram = C4PlantUmlExporterWithElementLinks(workspace, "master") + val diagram = C4PlantUmlExporterWithElementLinks(workspace, "/landscape/") .export(view) assertThat(diagram.definition.withoutHeaderAndFooter()).isEqualTo( """ System(System1, "System 1", "", ${'$'}tags="") - System(System2, "System 2", "", ${'$'}tags="")[[/master/system-2/context/]] + System(System2, "System 2", "", ${'$'}tags="")[[../system-2/context]] Rel_D(System2, System1, "uses", ${'$'}tags="") """.withoutTrailingSpaces() ) } - private fun createWorkspace(): Pair { + @Test + fun `link to other software system from two path segments deep`() { + val (workspace, view) = createWorkspaceWithTwoSystems() + + val diagram = C4PlantUmlExporterWithElementLinks(workspace, "/system-1/context/") + .export(view) + + assertThat(diagram.definition.withoutHeaderAndFooter()).isEqualTo( + """ + System(System1, "System 1", "", ${'$'}tags="") + System(System2, "System 2", "", ${'$'}tags="")[[../../system-2/context]] + + Rel_D(System2, System1, "uses", ${'$'}tags="") + """.withoutTrailingSpaces() + ) + } + + private fun createWorkspaceWithOneSystem(): Pair { + val workspace = Workspace("workspace name", "") + val system = workspace.model.addSoftwareSystem("System 1") + val view = workspace.views.createSystemContextView(system, "Context1", "") + .apply { addAllElements() } + + return workspace to view + } + + private fun createWorkspaceWithTwoSystems(): Pair { val workspace = Workspace("workspace name", "") val system = workspace.model.addSoftwareSystem("System 1") - return workspace to system + workspace.model.addSoftwareSystem("System 2").apply { uses(system, "uses") } + val view = workspace.views.createSystemContextView(system, "Context 1", "") + .apply { addAllElements() } + + return workspace to view } private fun String.withoutHeaderAndFooter() = this diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/ViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/ViewModelTest.kt index 572fcd05..62c031e0 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/ViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/ViewModelTest.kt @@ -10,7 +10,7 @@ import java.time.ZoneId import java.util.* abstract class ViewModelTest { - protected val svgFactory = { _: String -> "" } + protected val svgFactory = { _: String, _: String -> "" } protected fun generatorContext( workspaceName: String = "workspace name", From fe87a31237f0a31764cf8def4933e42c8e065645 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 15 Nov 2022 09:27:21 +0100 Subject: [PATCH 3/4] Generate html files concurrently This can greatly improve site generation for bigger sites with lots of views. --- .../site/generatr/site/SiteGenerator.kt | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt index d1ca4381..ac6e1adb 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt @@ -60,34 +60,42 @@ private fun copyAssets(assetsDir: File, exportDir: File) { private fun generateHtmlFiles(context: GeneratorContext, exportDir: File) { val branchDir = File(exportDir, context.currentBranch) - writeHtmlFile(branchDir, HomePageViewModel(context)) - writeHtmlFile(branchDir, WorkspaceDecisionsPageViewModel(context)) - writeHtmlFile(branchDir, SoftwareSystemsPageViewModel(context)) + buildList { + add { writeHtmlFile(branchDir, HomePageViewModel(context)) } + add { writeHtmlFile(branchDir, WorkspaceDecisionsPageViewModel(context)) } + add { writeHtmlFile(branchDir, SoftwareSystemsPageViewModel(context)) } - context.workspace.documentation.sections - .filter { it.order != 1 } - .forEach { writeHtmlFile(branchDir, WorkspaceDocumentationSectionPageViewModel(context, it)) } - context.workspace.documentation.decisions - .forEach { writeHtmlFile(branchDir, WorkspaceDecisionPageViewModel(context, it)) } + context.workspace.documentation.sections + .filter { it.order != 1 } + .forEach { + add { writeHtmlFile(branchDir, WorkspaceDocumentationSectionPageViewModel(context, it)) } + } + context.workspace.documentation.decisions + .forEach { + add { writeHtmlFile(branchDir, WorkspaceDecisionPageViewModel(context, it)) } + } - context.workspace.model.includedSoftwareSystems.forEach { - writeHtmlFile(branchDir, SoftwareSystemHomePageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemContextPageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemContainerPageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemComponentPageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemDeploymentPageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemDependenciesPageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemDecisionsPageViewModel(context, it)) - writeHtmlFile(branchDir, SoftwareSystemSectionsPageViewModel(context, it)) + context.workspace.model.includedSoftwareSystems.forEach { + add { writeHtmlFile(branchDir, SoftwareSystemHomePageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemContextPageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemContainerPageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemComponentPageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemDeploymentPageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemDependenciesPageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemDecisionsPageViewModel(context, it)) } + add { writeHtmlFile(branchDir, SoftwareSystemSectionsPageViewModel(context, it)) } - it.documentation.decisions.forEach { decision -> - writeHtmlFile(branchDir, SoftwareSystemDecisionPageViewModel(context, it, decision)) - } + it.documentation.decisions.forEach { decision -> + add { writeHtmlFile(branchDir, SoftwareSystemDecisionPageViewModel(context, it, decision)) } + } - it.documentation.sections.filter { section -> section.order != 1 }.forEach { section -> - writeHtmlFile(branchDir, SoftwareSystemSectionPageViewModel(context, it, section)) + it.documentation.sections.filter { section -> section.order != 1 }.forEach { section -> + add { writeHtmlFile(branchDir, SoftwareSystemSectionPageViewModel(context, it, section)) } + } } } + .parallelStream() + .forEach { it.invoke() } } private fun writeHtmlFile(exportDir: File, viewModel: PageViewModel) { From f91cc829c2569ddc2e9fc7dfc3fef3541b19997d Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 15 Nov 2022 11:15:32 +0100 Subject: [PATCH 4/4] Remove workspace field from C4PlantUmlExporterWithElementLinks Also some renames and argument ordering for consistency. --- .../site/generatr/StructurizrUtilities.kt | 5 +++- .../C4PlantUmlExporterWithElementLinks.kt | 6 ++--- .../site/generatr/site/DiagramGenerator.kt | 8 +++--- .../site/generatr/site/SiteGenerator.kt | 6 ++--- .../C4PlantUmlExporterWithElementLinksTest.kt | 26 +++++++++---------- 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/StructurizrUtilities.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/StructurizrUtilities.kt index 574fe5e7..12c99d57 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/StructurizrUtilities.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/StructurizrUtilities.kt @@ -5,4 +5,7 @@ import com.structurizr.model.Model import com.structurizr.model.SoftwareSystem val Model.includedSoftwareSystems: List - get() = softwareSystems.filter { it.location != Location.External } + get() = softwareSystems.filter { it.includedSoftwareSystem } + +val SoftwareSystem.includedSoftwareSystem + get () = this.location != Location.External diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt index dfd03984..85128de9 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinks.kt @@ -1,6 +1,5 @@ package nl.avisi.structurizr.site.generatr.site -import com.structurizr.Workspace import com.structurizr.export.Diagram import com.structurizr.export.IndentingWriter import com.structurizr.export.plantuml.C4PlantUMLExporter @@ -14,11 +13,10 @@ import com.structurizr.view.DynamicView import com.structurizr.view.SystemContextView import com.structurizr.view.SystemLandscapeView import com.structurizr.view.View -import nl.avisi.structurizr.site.generatr.includedSoftwareSystems +import nl.avisi.structurizr.site.generatr.includedSoftwareSystem import nl.avisi.structurizr.site.generatr.normalize class C4PlantUmlExporterWithElementLinks( - private val workspace: Workspace, private val url: String ): C4PlantUMLExporter() { companion object { @@ -46,7 +44,7 @@ class C4PlantUmlExporterWithElementLinks( } private fun Element.linkNeeded(view: View?) = - workspace.model.includedSoftwareSystems.contains(this) && this != view?.softwareSystem + this is SoftwareSystem && this.includedSoftwareSystem && this != view?.softwareSystem private fun setElementUrl(element: Element) { val path = "/${element.name.normalize()}/context/".asUrlRelativeTo(url) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt index deb05e06..bf545c09 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/DiagramGenerator.kt @@ -33,11 +33,11 @@ fun generateDiagrams(workspace: Workspace, exportDir: File) { } } -fun generateDiagramWithElementLinks(workspace: Workspace, exportDir: File, view: View, baseUrl: String): String { +fun generateDiagramWithElementLinks(view: View, url: String, exportDir: File): String { val pumlDir = pumlDir(exportDir) val svgDir = svgDir(exportDir) - val diagram = generatePlantUMLDiagramWithElementLinks(workspace, baseUrl, view) + val diagram = generatePlantUMLDiagramWithElementLinks(view, url) val name = "${diagram.key}-${view.key}" val plantUMLFile = File(pumlDir, "$name.puml") @@ -84,8 +84,8 @@ private fun readSvg(svgDir: File, name: String): String { return svgFile.readText() } -private fun generatePlantUMLDiagramWithElementLinks(workspace: Workspace, baseUrl: String, view: View ): Diagram { - val plantUMLExporter = C4PlantUmlExporterWithElementLinks(workspace, baseUrl) +private fun generatePlantUMLDiagramWithElementLinks(view: View, url: String): Diagram { + val plantUMLExporter = C4PlantUmlExporterWithElementLinks(url) return plantUMLExporter.export(view) } diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt index ac6e1adb..89c37887 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/SiteGenerator.kt @@ -45,9 +45,9 @@ fun generateSite( branches: List, currentBranch: String ) { - val generatorContext = GeneratorContext(version, workspace, branches, currentBranch) { name, baseUrl -> - val view = workspace.views.views.single { view -> view.key == name } - generateDiagramWithElementLinks(workspace, exportDir, view, baseUrl) + val generatorContext = GeneratorContext(version, workspace, branches, currentBranch) { key, url -> + val view = workspace.views.views.single { view -> view.key == key } + generateDiagramWithElementLinks(view, url, exportDir) } if (assetsDir != null) copyAssets(assetsDir, File(exportDir, currentBranch)) diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt index c3c0cc27..0def8a92 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/C4PlantUmlExporterWithElementLinksTest.kt @@ -10,9 +10,9 @@ class C4PlantUmlExporterWithElementLinksTest { @Test fun `renders diagram`() { - val (workspace, view) = createWorkspaceWithOneSystem() + val view = createWorkspaceWithOneSystem() - val diagram = C4PlantUmlExporterWithElementLinks(workspace, "/landscape/") + val diagram = C4PlantUmlExporterWithElementLinks("/landscape/") .export(view) assertThat(diagram.definition.withoutHeaderAndFooter()).isEqualTo( @@ -24,9 +24,9 @@ class C4PlantUmlExporterWithElementLinksTest { @Test fun `link to other software system`() { - val (workspace, view) = createWorkspaceWithTwoSystems() + val view = createWorkspaceWithTwoSystems() - val diagram = C4PlantUmlExporterWithElementLinks(workspace, "/landscape/") + val diagram = C4PlantUmlExporterWithElementLinks("/landscape/") .export(view) assertThat(diagram.definition.withoutHeaderAndFooter()).isEqualTo( @@ -41,9 +41,9 @@ class C4PlantUmlExporterWithElementLinksTest { @Test fun `link to other software system from two path segments deep`() { - val (workspace, view) = createWorkspaceWithTwoSystems() + val view = createWorkspaceWithTwoSystems() - val diagram = C4PlantUmlExporterWithElementLinks(workspace, "/system-1/context/") + val diagram = C4PlantUmlExporterWithElementLinks("/system-1/context/") .export(view) assertThat(diagram.definition.withoutHeaderAndFooter()).isEqualTo( @@ -56,23 +56,21 @@ class C4PlantUmlExporterWithElementLinksTest { ) } - private fun createWorkspaceWithOneSystem(): Pair { + private fun createWorkspaceWithOneSystem(): SystemContextView { val workspace = Workspace("workspace name", "") val system = workspace.model.addSoftwareSystem("System 1") - val view = workspace.views.createSystemContextView(system, "Context1", "") - .apply { addAllElements() } - return workspace to view + return workspace.views.createSystemContextView(system, "Context1", "") + .apply { addAllElements() } } - private fun createWorkspaceWithTwoSystems(): Pair { + private fun createWorkspaceWithTwoSystems(): SystemContextView { val workspace = Workspace("workspace name", "") val system = workspace.model.addSoftwareSystem("System 1") workspace.model.addSoftwareSystem("System 2").apply { uses(system, "uses") } - val view = workspace.views.createSystemContextView(system, "Context 1", "") - .apply { addAllElements() } - return workspace to view + return workspace.views.createSystemContextView(system, "Context 1", "") + .apply { addAllElements() } } private fun String.withoutHeaderAndFooter() = this